diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..654eb00a --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +# GitHub Personal Access Token for Feedback Widget +# Create a token at: https://github.com/settings/tokens/new +# Required scopes: public_repo (for public repositories) +GITHUB_TOKEN=ghp_your_github_token_here diff --git a/FEEDBACK_INTEGRATION_GUIDE.md b/FEEDBACK_INTEGRATION_GUIDE.md new file mode 100644 index 00000000..33bba546 --- /dev/null +++ b/FEEDBACK_INTEGRATION_GUIDE.md @@ -0,0 +1,30 @@ +# Feedback Widget - GitHub Issues Integration + +The feedback widget is **fully integrated with GitHub Issues**. When users submit feedback, it automatically creates an issue in your repository. + +## 🚀 Quick Setup (5 minutes) + +### Step 1: Create a GitHub Personal Access Token + +1. Go to: https://github.com/settings/tokens/new +2. Give it a name: "Interledger Docs Feedback" +3. Select expiration (recommend: 90 days or No expiration) +4. Select scopes: **`public_repo`** (for public repositories) +5. Click "Generate token" +6. **Copy the token** (you won't see it again!) + +### Step 2: Add Token to Environment Variables + +Create a `.env` file in your project root: + +```bash +cp .env.example .env +``` + +Edit `.env` and add your token: + +```env +GITHUB_TOKEN=ghp_your_actual_token_here +``` + +⚠️ **Important:** Make sure `.env` is in your `.gitignore` (it should be by default) diff --git a/astro.config.mjs b/astro.config.mjs index 26373eab..431499c1 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -6,6 +6,7 @@ import mdx from "@astrojs/mdx"; // https://astro.build/config export default defineConfig({ + output: 'server', site: "https://interledger.org", base: "/developers", i18n: { @@ -62,6 +63,7 @@ export default defineConfig({ components: { Header: "./src/components/Header.astro", PageSidebar: "./src/components/PageSidebar.astro", + Footer: "./src/components/Footer.astro", }, social: { github: "https://github.com/interledger", diff --git a/src/components/FeedbackWidget.astro b/src/components/FeedbackWidget.astro new file mode 100644 index 00000000..ec019123 --- /dev/null +++ b/src/components/FeedbackWidget.astro @@ -0,0 +1,324 @@ +--- +// Simple feedback widget that works without external services +// Feedback can be sent via GitHub Issues or collected locally +const { lang = 'en' } = Astro.props; + +const translations = { + en: { + question: "Was this page helpful?", + yes: "Yes", + no: "No", + thanks: "Thanks for your feedback!", + improve: "Help us improve", + improvePlaceholder: "What can we improve?", + submit: "Submit", + submitting: "Submitting...", + }, + es: { + question: "¿Fue útil esta página?", + yes: "Sí", + no: "No", + thanks: "¡Gracias por tu comentario!", + improve: "Ayúdanos a mejorar", + improvePlaceholder: "¿Qué podemos mejorar?", + submit: "Enviar", + submitting: "Enviando...", + } +}; + +const t = translations[lang as keyof typeof translations] || translations.en; +--- + +
+
+

{t.question}

+ +
+ + + + +
+ + + + diff --git a/src/components/Footer.astro b/src/components/Footer.astro new file mode 100644 index 00000000..b14c5a05 --- /dev/null +++ b/src/components/Footer.astro @@ -0,0 +1,12 @@ +--- +import type { Props } from '@astrojs/starlight/props'; +import Default from '@astrojs/starlight/components/Footer.astro'; +import FeedbackWidget from './FeedbackWidget.astro'; +import { getLangFromUrl } from '../i18n/utils'; + +const lang = getLangFromUrl(Astro.url); +--- + + + + diff --git a/src/components/PageSidebar.astro b/src/components/PageSidebar.astro index 8561a284..18f4f09f 100644 --- a/src/components/PageSidebar.astro +++ b/src/components/PageSidebar.astro @@ -10,7 +10,7 @@ const toc = noOverview && Astro.props.toc !== undefined ? { ...Astro.props.toc, items: Astro.props.toc?.items.slice(1), - } + } : Astro.props.toc; --- diff --git a/src/pages/api/feedback.ts b/src/pages/api/feedback.ts new file mode 100644 index 00000000..a0d67ecc --- /dev/null +++ b/src/pages/api/feedback.ts @@ -0,0 +1,99 @@ +import type { APIRoute } from 'astro'; + +// Mark this endpoint as server-rendered +export const prerender = false; + +export const POST: APIRoute = async ({ request }) => { + try { + const data = await request.json(); + const { type, page, message } = data; + + // Get GitHub token from environment variable + const GITHUB_TOKEN = import.meta.env.GITHUB_TOKEN; + const GITHUB_REPO = 'interledger/documentation-feedback'; + + if (!GITHUB_TOKEN) { + console.error('GITHUB_TOKEN not configured'); + return new Response( + JSON.stringify({ + success: false, + error: 'GitHub token not configured' + }), + { + status: 500, + headers: { 'Content-Type': 'application/json' } + } + ); + } + + // Create issue title and body + const emoji = type === 'yes' ? '👍' : '👎'; + const sentiment = type === 'yes' ? 'Positive' : 'Negative'; + const issueTitle = `[Feedback] ${emoji} ${page}`; + + const issueBody = `**Page:** ${page} +**Feedback Type:** ${sentiment} ${emoji} +**User Message:** + +${message || '_No additional feedback provided_'} + +--- +_Submitted via feedback widget on ${new Date().toISOString()}_`; + + // Create GitHub issue + const response = await fetch( + `https://api.github.com/repos/${GITHUB_REPO}/issues`, + { + method: 'POST', + headers: { + 'Authorization': `token ${GITHUB_TOKEN}`, + 'Accept': 'application/vnd.github.v3+json', + 'Content-Type': 'application/json', + 'User-Agent': 'Interledger-Docs-Feedback-Widget' + }, + body: JSON.stringify({ + title: issueTitle, + body: issueBody, + labels: [ + 'feedback', + 'docs', + type === 'yes' ? 'feedback-positive' : 'feedback-negative' + ] + }) + } + ); + + if (!response.ok) { + const errorData = await response.text(); + console.error('GitHub API error:', errorData); + throw new Error(`GitHub API returned ${response.status}`); + } + + const issue = await response.json(); + + return new Response( + JSON.stringify({ + success: true, + issueUrl: issue.html_url, + issueNumber: issue.number + }), + { + status: 200, + headers: { 'Content-Type': 'application/json' } + } + ); + + } catch (error) { + console.error('Error creating GitHub issue:', error); + return new Response( + JSON.stringify({ + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }), + { + status: 500, + headers: { 'Content-Type': 'application/json' } + } + ); + } +};