From 500ec113ad5ce951adfee7917b0b020a164049a2 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 11:41:23 +0530 Subject: [PATCH 01/24] fix tooltips, remove unwanted files, fetch sentimentData directly and remove mock, arrange files --- index.html | 4 +- package.json | 2 +- src/App.tsx | 6 +- .../constants.ts | 0 .../dashboard/Analytics/SentimentHeatmap.tsx | 116 +++++++++--------- .../DashboardView/DashboardContent.tsx | 3 +- src/components/index/DashboardContent.tsx | 116 ------------------ src/components/onboarding/CategorySelect.tsx | 2 +- src/components/onboarding/OnboardingForm.tsx | 8 +- .../onboarding/database/AirbyteConfig.tsx | 4 +- .../onboarding/database/AirbyteSyncButton.tsx | 4 +- .../onboarding/database/GroqConnection.tsx | 4 +- .../database/MotherDuckConnection.tsx | 2 +- src/components/{index => pages}/Dashboard.tsx | 0 .../{index => pages}/Onboarding.tsx | 0 .../{index => pages}/WelcomeScreen.tsx | 0 src/{ => config}/data/businessCategories.ts | 0 src/config/services/index.ts | 4 + .../useAirbyteSyncStatus.ts} | 6 +- .../useConnectionStatus.ts | 6 +- src/{lib/groq => hooks}/useGroqConnection.ts | 2 +- ...therDuck.ts => useMotherDuckConnection.ts} | 0 src/lib/airbyte/service.ts | 6 +- src/lib/groq/models.ts | 30 +++-- src/lib/motherduck/queries/analytics.ts | 10 +- src/lib/motherduck/queries/keyPhrases.ts | 45 ------- .../motherduck/queries/sentimentInsights.ts | 79 ++++++++++++ src/lib/motherduck/types.ts | 2 + vite.config.ts | 10 +- 29 files changed, 204 insertions(+), 267 deletions(-) rename src/components/common/{LoadingStages => LoadingSpinner}/constants.ts (100%) delete mode 100644 src/components/index/DashboardContent.tsx rename src/components/{index => pages}/Dashboard.tsx (100%) rename src/components/{index => pages}/Onboarding.tsx (100%) rename src/components/{index => pages}/WelcomeScreen.tsx (100%) rename src/{ => config}/data/businessCategories.ts (100%) create mode 100644 src/config/services/index.ts rename src/{lib/airbyte/useSyncStatus.ts => hooks/useAirbyteSyncStatus.ts} (91%) rename src/{lib/motherduck => hooks}/useConnectionStatus.ts (90%) rename src/{lib/groq => hooks}/useGroqConnection.ts (96%) rename src/hooks/{useMotherDuck.ts => useMotherDuckConnection.ts} (100%) delete mode 100644 src/lib/motherduck/queries/keyPhrases.ts create mode 100644 src/lib/motherduck/queries/sentimentInsights.ts diff --git a/index.html b/index.html index ef4901a..9c21edc 100644 --- a/index.html +++ b/index.html @@ -6,8 +6,8 @@ - Analysr - Business Analytics Platform - + Analysr + diff --git a/package.json b/package.json index 1dd258b..f813bc7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "0.3.0", + "version": "1.0.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src/App.tsx b/src/App.tsx index 284471b..a37f4a3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; -import WelcomeScreen from "./components/index/WelcomeScreen"; -import Onboarding from "./components/index/Onboarding"; -import Dashboard from "./components/index/Dashboard"; +import WelcomeScreen from "./components/pages/WelcomeScreen"; +import Onboarding from "./components/pages/Onboarding"; +import Dashboard from "./components/pages/Dashboard"; function App() { return ( diff --git a/src/components/common/LoadingStages/constants.ts b/src/components/common/LoadingSpinner/constants.ts similarity index 100% rename from src/components/common/LoadingStages/constants.ts rename to src/components/common/LoadingSpinner/constants.ts diff --git a/src/components/dashboard/Analytics/SentimentHeatmap.tsx b/src/components/dashboard/Analytics/SentimentHeatmap.tsx index a709120..27d61e1 100644 --- a/src/components/dashboard/Analytics/SentimentHeatmap.tsx +++ b/src/components/dashboard/Analytics/SentimentHeatmap.tsx @@ -2,26 +2,14 @@ import { motion } from 'framer-motion'; import { Heart } from 'lucide-react'; import { ResponsiveContainer, Treemap, Tooltip } from 'recharts'; import TooltipComponent from '../../common/Tooltip/Tooltip'; +import NoDataFallback from '../../common/DataInfo/NoDataFallback'; -interface SentimentData { +export interface SentimentData { name: string; size: number; sentiment: number; } -const SENTIMENT_WORDS: SentimentData[] = [ - { name: 'Love', size: 800, sentiment: 1 }, - { name: 'Amazing', size: 700, sentiment: 0.9 }, - { name: 'Great', size: 600, sentiment: 0.8 }, - { name: 'Good', size: 500, sentiment: 0.6 }, - { name: 'Okay', size: 400, sentiment: 0.2 }, - { name: 'Neutral', size: 300, sentiment: 0 }, - { name: 'Poor', size: 250, sentiment: -0.4 }, - { name: 'Bad', size: 200, sentiment: -0.6 }, - { name: 'Terrible', size: 150, sentiment: -0.8 }, - { name: 'Hate', size: 100, sentiment: -1 } -]; - const getSentimentColor = (sentiment: number) => { if (sentiment > 0.5) return '#22c55e'; // Green for very positive if (sentiment > 0) return '#86efac'; // Light green for positive @@ -30,10 +18,54 @@ const getSentimentColor = (sentiment: number) => { return '#ef4444'; // Red for very negative }; -export default function SentimentHeatmap() { - const data = { +const CustomizedContent = (props: any) => { + const { x, y, width, height, name, sentiment } = props; + + return ( + + + {width > 40 && height > 25 && ( + + {name} + + )} + + ); +}; + +interface SentimentHeatmapProps { + data: SentimentData[]; +} + +export default function SentimentHeatmap({ data }: SentimentHeatmapProps) { + if (!data || data.length === 0) { + return ; + } + + const transformedData = { name: 'sentiment', - children: SENTIMENT_WORDS + children: data.map(item => ({ + name: item.name, + value: item.size, + size: item.size, + sentiment: item.sentiment + })) }; return ( @@ -48,44 +80,15 @@ export default function SentimentHeatmap() { -
- +
+ { - const { depth, x, y, width, height, name, sentiment } = props; - - if (depth === 1) { - return ( - - - {width > 30 && height > 30 && ( - - {name} - - )} - - ); - } - return null; - }} + data={transformedData.children} + dataKey="value" + aspectRatio={4/3} + stroke="#1f2937" + animationDuration={450} + content={} > { @@ -94,6 +97,9 @@ export default function SentimentHeatmap() { return (

{data.name}

+

+ Frequency: {data.size} +

Sentiment: {(data.sentiment * 100).toFixed(0)}%

@@ -106,4 +112,4 @@ export default function SentimentHeatmap() {
); -} +} \ No newline at end of file diff --git a/src/components/dashboard/DashboardView/DashboardContent.tsx b/src/components/dashboard/DashboardView/DashboardContent.tsx index 56da2fc..e5a2d4b 100644 --- a/src/components/dashboard/DashboardView/DashboardContent.tsx +++ b/src/components/dashboard/DashboardView/DashboardContent.tsx @@ -26,7 +26,6 @@ export default function DashboardContent({ if (!analyticsData || !analyticsData.totalReviews || !stack || !substack) { return ; } - return (
} > - +
import("../dashboard/Analytics/StatGrid")); -const PositiveInsights = lazy( - () => import("../dashboard/Analytics/PositiveInsights") -); -const KeyPhraseAnalysis = lazy( - () => import("../dashboard/TextAnalysis/KeyPhraseAnalysis") -); -const AspectAnalysis = lazy( - () => import("../dashboard/Analytics/AspectAnalysis") -); -const NegativeInsights = lazy( - () => import("../dashboard/Analytics/NegativeInsights") -); -const TextAnalysis = lazy(() => import("../dashboard/TextAnalysis")); -const BusinessInsights = lazy( - () => import("../dashboard/GPTInsights/BusinessInsights") -); - -interface DashboardContentProps { - analyticsData: ProcessedAnalytics | null; - stack?: string; - substack?: string; - groqToken?: string; -} - -export default function DashboardContent({ - analyticsData, - stack, - substack, - groqToken, -}: DashboardContentProps) { - if (!analyticsData || !analyticsData.totalReviews) { - return ; - } - - return ( -
- - } - > - - - - - } - > - - - - - } - > - {analyticsData?.positiveInsights && ( - - )} - - -
- - } - > - {analyticsData?.keyPhrases && ( - - )} - - - - } - > - {analyticsData?.aspectAnalysis && ( - - )} - -
- - - } - > - {analyticsData?.textAnalysis && ( - - )} - - - - } - > - {analyticsData?.negativeInsights && ( - - )} - -
- ); -} diff --git a/src/components/onboarding/CategorySelect.tsx b/src/components/onboarding/CategorySelect.tsx index 4ef1381..a76f58c 100644 --- a/src/components/onboarding/CategorySelect.tsx +++ b/src/components/onboarding/CategorySelect.tsx @@ -1,7 +1,7 @@ import { useState, useEffect, useRef } from "react"; import { motion } from "framer-motion"; import { ChevronDown } from "lucide-react"; -import type { BusinessCategory, SubStack } from "../../data/businessCategories"; +import type { BusinessCategory, SubStack } from "../../config/data/businessCategories"; interface CategorySelectProps { categories: BusinessCategory[]; diff --git a/src/components/onboarding/OnboardingForm.tsx b/src/components/onboarding/OnboardingForm.tsx index b9d8f27..0b3d7d4 100644 --- a/src/components/onboarding/OnboardingForm.tsx +++ b/src/components/onboarding/OnboardingForm.tsx @@ -4,7 +4,7 @@ import { useRef, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import CategorySelect from "./CategorySelect"; import DatabaseConnection from "./DatabaseConnection"; -import { businessCategories } from "../../data/businessCategories"; +import { businessCategories } from "../../config/data/businessCategories"; import { useOnboardingForm } from "../../hooks/useOnboardingForm"; import Tooltip from "../common/Tooltip/Tooltip"; @@ -172,7 +172,7 @@ export default function OnboardingForm() {
- + Business Category
- + Areas of Interest diff --git a/src/components/onboarding/database/AirbyteConfig.tsx b/src/components/onboarding/database/AirbyteConfig.tsx index 686202a..5194bbe 100644 --- a/src/components/onboarding/database/AirbyteConfig.tsx +++ b/src/components/onboarding/database/AirbyteConfig.tsx @@ -30,7 +30,7 @@ export default function AirbyteConfig({

Optional Airbyte Configuration

@@ -73,7 +73,7 @@ export default function AirbyteConfig({ }`} /> diff --git a/src/components/onboarding/database/AirbyteSyncButton.tsx b/src/components/onboarding/database/AirbyteSyncButton.tsx index a4740bc..aee067c 100644 --- a/src/components/onboarding/database/AirbyteSyncButton.tsx +++ b/src/components/onboarding/database/AirbyteSyncButton.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { motion } from 'framer-motion'; import { RefreshCw, ChevronDown } from 'lucide-react'; -import { useSyncStatus } from '../../../lib/airbyte/useSyncStatus'; +import { useAirbyteSyncStatus } from '../../../hooks/useAirbyteSyncStatus'; import StatusIndicator from '../../common/ProgressLoader/StatusIndicator'; interface AirbyteSyncButtonProps { @@ -15,7 +15,7 @@ export default function AirbyteSyncButton({ token, connectionId }: AirbyteSyncBu const [isTriggered, setIsTriggered] = useState(false); const [showDropdown, setShowDropdown] = useState(false); const [selectedJobType, setSelectedJobType] = useState('sync'); - const syncStatus = useSyncStatus( + const syncStatus = useAirbyteSyncStatus( isTriggered ? token : undefined, isTriggered ? connectionId : undefined, selectedJobType, diff --git a/src/components/onboarding/database/GroqConnection.tsx b/src/components/onboarding/database/GroqConnection.tsx index ff08ae9..e9a2d69 100644 --- a/src/components/onboarding/database/GroqConnection.tsx +++ b/src/components/onboarding/database/GroqConnection.tsx @@ -3,7 +3,7 @@ import { Brain, Loader2 } from "lucide-react"; import { motion, AnimatePresence } from "framer-motion"; import StatusIndicator from "../../common/ProgressLoader/StatusIndicator"; import Tooltip from "../../common/Tooltip/Tooltip"; -import { useGroqConnection } from "../../../lib/groq/useGroqConnection"; +import { useGroqConnection } from "../../../hooks/useGroqConnection"; interface GroqConnectionProps { token?: string; @@ -32,7 +32,7 @@ export default function GroqConnection({ return (
- + GROQ Configuration ({ diff --git a/src/hooks/useMotherDuck.ts b/src/hooks/useMotherDuckConnection.ts similarity index 100% rename from src/hooks/useMotherDuck.ts rename to src/hooks/useMotherDuckConnection.ts diff --git a/src/lib/airbyte/service.ts b/src/lib/airbyte/service.ts index e198346..7dd346d 100644 --- a/src/lib/airbyte/service.ts +++ b/src/lib/airbyte/service.ts @@ -1,6 +1,5 @@ import type { AirbyteConfig, SyncJobResponse } from "./types"; - -const API_BASE_URL = "/api/airbyte"; +import { AIRBYTE_API_PROXY_URL as API_BASE_URL } from "../../config/services"; export async function checkConnectionStatus( config: AirbyteConfig @@ -22,8 +21,7 @@ export async function checkConnectionStatus( throw new Error(`Connection check failed: ${response.statusText}`); } - const data = await response.json(); - console.log(data); + await response.json(); return response.ok; } catch (error) { console.error("Connection check error:", error); diff --git a/src/lib/groq/models.ts b/src/lib/groq/models.ts index af4bc7e..486ac85 100644 --- a/src/lib/groq/models.ts +++ b/src/lib/groq/models.ts @@ -1,3 +1,4 @@ +import { GROQ_API_PROXY_URL } from '../../config/services'; import type { GroqModel } from './types'; export const GROQ_MODELS: GroqModel[] = [ @@ -5,32 +6,35 @@ export const GROQ_MODELS: GroqModel[] = [ id: 'mixtral-8x7b-32768', name: 'Mixtral 8x7B', description: 'Balanced performance for business analysis', - maxTokens: 32768 - } + maxTokens: 32768, + }, ]; export async function fetchAvailableModels(token: string): Promise { try { - const response = await fetch('https://api.groq.com/v1/models', { + const response = await fetch(`${GROQ_API_PROXY_URL}/v1/models`, { headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, }); if (!response.ok) { - throw new Error('Failed to fetch GROQ models'); + const errorData = await response.json().catch(() => null); + throw new Error( + errorData?.message || `Failed to fetch GROQ models: ${response.statusText}` + ); } const data = await response.json(); - return data.data.map((model: any) => ({ id: model.id, - name: model.id.split('-').map((word: string) => - word.charAt(0).toUpperCase() + word.slice(1) - ).join(' '), - description: 'AI model for analysis', - maxTokens: model.context_window || 8192 + name: model.id + .split('-') + .map((word: string) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '), + description: model.description || 'AI model for analysis', + maxTokens: model.context_window || 8192, })); } catch (error) { console.error('Failed to fetch GROQ models:', error); diff --git a/src/lib/motherduck/queries/analytics.ts b/src/lib/motherduck/queries/analytics.ts index 85954ba..fe2cdac 100644 --- a/src/lib/motherduck/queries/analytics.ts +++ b/src/lib/motherduck/queries/analytics.ts @@ -6,6 +6,7 @@ import { fetchPositiveInsights } from './positiveInsights'; import { fetchTextAnalysis } from './textAnalysis'; import type { ProcessedAnalytics } from '../types'; import type { DataLimit } from '../../../components/onboarding/DataSelectionStep'; +import { fetchSentimentInsights } from './sentimentInsights'; export async function fetchAnalytics( database: string, @@ -35,13 +36,15 @@ export async function fetchAnalytics( aspectAnalysis, negativeInsights, positiveInsights, - textAnalysis + textAnalysis, + sentimentData ] = await Promise.all([ connection.evaluateQuery(basicStatsQuery), fetchAspectAnalysis(database, tableName, limit), fetchNegativeInsights(database, tableName, limit), fetchPositiveInsights(database, tableName, limit), - fetchTextAnalysis(database, tableName, limit) + fetchTextAnalysis(database, tableName, limit), + fetchSentimentInsights(database, tableName, limit), ]); const stats = basicStats.data.toRows()[0]; @@ -56,7 +59,8 @@ export async function fetchAnalytics( aspectAnalysis, negativeInsights, positiveInsights, - textAnalysis + textAnalysis, + sentimentInsights: sentimentData, }; } catch (error) { console.error('Analytics query execution failed:', error); diff --git a/src/lib/motherduck/queries/keyPhrases.ts b/src/lib/motherduck/queries/keyPhrases.ts deleted file mode 100644 index d34405c..0000000 --- a/src/lib/motherduck/queries/keyPhrases.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { getConnection } from '../connection'; -import { getTableReference } from './utils'; -import type { KeyPhrase } from '../../../types/analytics'; -import type { Dataset } from '../types'; - -export async function fetchKeyPhrases(dataset: Dataset, limit: number | 'All'): Promise { - const connection = await getConnection(); - const limitClause = limit === 'All' ? '' : `LIMIT ${limit}`; - const tableRef = getTableReference(dataset); - - const query = ` - WITH review_stats AS ( - SELECT - CASE - WHEN stars >= 4 THEN 'excellent service' - WHEN stars = 3 THEN 'average experience' - ELSE 'poor quality' - END as phrase, - COUNT(*) as occurrences, - AVG(CASE - WHEN stars >= 4 THEN 1 - WHEN stars <= 2 THEN -1 - ELSE 0 - END) as sentiment - FROM ${tableRef} - GROUP BY - CASE - WHEN stars >= 4 THEN 'excellent service' - WHEN stars = 3 THEN 'average experience' - ELSE 'poor quality' - END - HAVING COUNT(*) >= 5 - ORDER BY occurrences DESC - ${limitClause} - ) - SELECT * FROM review_stats - `; - - const result = await connection.evaluateQuery(query); - return result.data.toRows().map(row => ({ - text: String(row.phrase), - occurrences: Number(row.occurrences), - sentiment: Number(row.sentiment) - })); -} diff --git a/src/lib/motherduck/queries/sentimentInsights.ts b/src/lib/motherduck/queries/sentimentInsights.ts new file mode 100644 index 0000000..240e921 --- /dev/null +++ b/src/lib/motherduck/queries/sentimentInsights.ts @@ -0,0 +1,79 @@ +import { getConnection } from '../connection'; +import { getTableRef } from './utils'; +import type { DataLimit } from '../../../components/onboarding/DataSelectionStep'; +import type { SentimentData } from '../../../components/dashboard/Analytics/SentimentHeatmap'; + + +export async function fetchSentimentInsights( + database: string, + tableName: string, + limit: DataLimit + ): Promise { + const connection = await getConnection(); + const tableRef = getTableRef(database, tableName); + const limitClause = limit === 'All' ? '' : `LIMIT ${limit}`; + + try { + const query = ` + WITH sample_data AS ( + SELECT review_text, stars + FROM ${tableRef} + WHERE review_text IS NOT NULL + AND stars IS NOT NULL + ORDER BY RANDOM() + ${limitClause} + ), + words AS ( + SELECT + word.value as word, + stars + FROM sample_data, + UNNEST(string_split( + regexp_replace(lower(review_text), '[^a-z\s]', ' ', 'g'), + ' ' + )) as word(value) + WHERE strlen(trim(word.value)) > 3 + ), + word_sentiments AS ( + SELECT + word, + COUNT(*) as frequency, + AVG(CASE + WHEN stars >= 4 THEN 1 + WHEN stars = 3 THEN 0 + ELSE -1 + END) as sentiment_score + FROM words + WHERE word NOT IN ( + 'this', 'that', 'with', 'from', 'what', 'where', 'when', + 'have', 'here', 'there', 'they', 'their', 'were', 'would', + 'could', 'should', 'about', 'which', 'thing', 'some', 'these' + ) + GROUP BY word + HAVING COUNT(*) >= 5 -- Reduced minimum frequency for testing + ) + SELECT + word as name, + frequency as size, + ROUND(CAST( + (sentiment_score + 1) / 2 as DOUBLE + ), 2) as sentiment + FROM word_sentiments + WHERE frequency > 0 + ORDER BY frequency DESC + LIMIT 30 + `; + + const result = await connection.evaluateQuery(query); + const rows = result.data.toRows(); + + return rows.map((row) => ({ + name: String(row.name), + size: Number(row.size), + sentiment: Number(row.sentiment) + })); + } catch (error) { + console.error('Sentiment analysis error:', error); + return []; + } + } \ No newline at end of file diff --git a/src/lib/motherduck/types.ts b/src/lib/motherduck/types.ts index d8d5e96..b51f350 100644 --- a/src/lib/motherduck/types.ts +++ b/src/lib/motherduck/types.ts @@ -1,3 +1,4 @@ +import { SentimentData } from "../../components/dashboard/Analytics/SentimentHeatmap"; import { AspectAnalysis, PositiveInsight, @@ -22,6 +23,7 @@ export interface ProcessedAnalytics { negativeInsights: NegativeInsight[]; positiveInsights: PositiveInsight[]; textAnalysis: TextAnalysis; + sentimentInsights?: SentimentData[]; } export type { AspectAnalysis }; diff --git a/vite.config.ts b/vite.config.ts index a39c908..f7ae6da 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import path from 'path'; import { fixRequestBody } from 'http-proxy-middleware'; +import { AIRBYTE_API_BASE_URL, AIRBYTE_API_PROXY_URL, GROQ_API_BASE_URL, GROQ_API_PROXY_URL } from './src/config/services/index'; export default defineConfig({ plugins: [react()], @@ -13,16 +14,16 @@ export default defineConfig({ 'Cross-Origin-Embedder-Policy': 'require-corp', }, proxy: { - '/api/airbyte': { - target: 'https://api.airbyte.com/v1', + [AIRBYTE_API_PROXY_URL]: { + target: AIRBYTE_API_BASE_URL, changeOrigin: true, rewrite: (path) => path.replace(/^\/api\/airbyte/, ''), configure: (proxy, _options) => { proxy.on('proxyReq', fixRequestBody); }, }, - '/api/groq': { - target: 'http://localhost:3000', + [GROQ_API_PROXY_URL]: { + target: GROQ_API_BASE_URL, changeOrigin: true, rewrite: (path) => path.replace(/^\/api\/groq/, ''), configure: (proxy, _options) => { @@ -37,3 +38,4 @@ export default defineConfig({ }, }, }); + From 5871c841f911f161a5a8c36128640d76ca84d2e3 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 11:56:57 +0530 Subject: [PATCH 02/24] update no cache --- src/components/welcome/WelcomeHero.tsx | 2 +- src/lib/airbyte/service.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/welcome/WelcomeHero.tsx b/src/components/welcome/WelcomeHero.tsx index 39e44a2..ce0a339 100644 --- a/src/components/welcome/WelcomeHero.tsx +++ b/src/components/welcome/WelcomeHero.tsx @@ -46,7 +46,7 @@ export default function WelcomeHero() { flexDirection: "row", alignItems: "center", }} - className="w-20 mt-8 p-0 md:p-2 bg-gradient-to-r from-blue-500/5 via-purple-500/5 to-blue-500/5 backdrop-blur-sm border border-white/5 rounded-xl" + className="sm:hidden w-20 mt-8 p-0 md:p-2 bg-gradient-to-r from-blue-500/5 via-purple-500/5 to-blue-500/5 backdrop-blur-sm border border-white/5 rounded-xl" >

{welcomeScreenData.welcomeSectionVersionBottom} diff --git a/src/lib/airbyte/service.ts b/src/lib/airbyte/service.ts index 7dd346d..74b47e1 100644 --- a/src/lib/airbyte/service.ts +++ b/src/lib/airbyte/service.ts @@ -12,6 +12,7 @@ export async function checkConnectionStatus( const response = await fetch(`${API_BASE_URL}/sources`, { method: "GET", headers: { + 'Cache-Control': 'no-cache', authorization: `Bearer ${config.bearerToken}`, accept: "application/json", }, @@ -41,6 +42,7 @@ export async function triggerJob( const response = await fetch(`${API_BASE_URL}/jobs`, { method: "POST", headers: { + 'Cache-Control': 'no-cache', authorization: `Bearer ${config.bearerToken}`, accept: "application/json", "content-type": "application/json", From 89a0bd58a7711ef7af58bc90e53a0ab2c7e1fde8 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 12:26:24 +0530 Subject: [PATCH 03/24] update readme --- README.md | 87 +++++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 1064dbe..65db515 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,47 @@ -# Analysr - Business Analytics Platform 📊 +# Analysr - Review Analytics Platform 📊 +

Analysr Banner

- Transform your business with powerful analytics and data-driven insights + Transform your business with Analysr

+## 1️⃣ One Liner +This is my submission for Airbyte-Motherduck Hackathon - December 2024 - January 2025 + +For all you speedy folks out there, here’s the scoop: + +- **1.0.0** + With your customer reviews in motherduck, along with your chosen business stack and areas of interest, Analysr is ready to dish out some insightful analytics. And just to sweeten the deal, Groq has stepped in to help you navigate all your growth phases—because who doesn’t want a sidekick in business? + +Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for those feelings), Advanced Text Analysis, Groq Business Analytics, Keyphrase Analysis, and a handy Competitor Comparison. + +## Walkthrough + +1) To obtain customer review insights, sync your data to Motherduck with the schema: { "review_text": "string", "stars": "number" } (More schemas will be supported soon). We recommend using Airbyte due to its extensive list of sources and seamless data movement. +2) Visit the Analysr website at (growwithanalysr.web.app) and click on the "Get Started Now" button. +3) Select your business stack and substack; Groq and queries will use this information to fetch insights. +4) Input your Motherduck token and wait for the connection to be established (the time required will depend on your network bandwidth). +5) Select the database and table where your customer reviews or any related reviews exist, and set the data limit. +6) Input your Groq token (recommended) to obtain AI-based insights. +7) Optionally provide your Airbyte bearer token (from the cloud.airbyte.com settings page) and connection ID (from the connections tab URL) to trigger a sync for updating your Motherduck table. +8) Finally, input your area of interest for insights, such as customer satisfaction, and click "Continue to Dashboard." +9) Wait a few seconds until all queries are executed and visualized. +10) Voilà! Your dashboard will be ready, featuring all the capabilities of Analysr to support your next big step! + + + ## ✨ Features -### 🎯 Smart Business Insights -- AI-powered analysis of customer feedback and market trends -- Sentiment analysis and emotion tracking -- Key phrase extraction and topic modeling -- Competitive benchmarking and industry comparisons - -### 📈 Advanced Analytics -- Real-time data processing and visualization -- Customizable dashboards and reports -- Trend analysis and forecasting -- Performance metrics and KPIs - -### 🔍 Deep Customer Understanding -- Customer sentiment tracking -- Demographic analysis -- Behavior pattern recognition -- Satisfaction metrics and NPS tracking - -### 🚀 Growth Opportunities -- Market opportunity identification -- Customer pain point analysis -- Competitive advantage analysis -- Strategic recommendations +- **Aspect Analysis:** Gain insights into different aspects of customer feedback. +- **Word Sentiment Heatmap:** Visualize sentiment trends in your reviews. +- **Advanced Text Analysis:** Delve deeper into the nuances of customer language. +- **Groq Business Analytics:** Access data-driven insights to inform your growth strategy. +- **Keyphrase Analysis:** Identify and analyze key phrases that matter to your customers. +- **Competitor Comparison:** Benchmark your performance against competitors. ## 🛠️ Technology Stack @@ -42,39 +51,29 @@ - **Visualization**: Recharts - **State Management**: Zustand - **Animations**: Framer Motion +- **Hosting**: Firebase +- **CI/CD**: GitHub Actions for automated deployment ## 🚀 Getting Started 1. Clone the repository: + ```bash -git clone https://github.com/yourusername/analysr.git +git clone https://github.com/btkcodedev/airbyte-motherduck-hackathon.git ``` 2. Install dependencies: + ```bash npm install ``` -3. Set up environment variables: -```env -MOTHERDUCK_TOKEN=your_token -GROQ_API_KEY=your_key -``` +3. Start the development server: -4. Start the development server: ```bash npm run dev ``` -## 📊 Dashboard Features - -- **Real-time Analytics**: Monitor business performance in real-time -- **Sentiment Analysis**: Track customer sentiment and emotions -- **Competitive Analysis**: Compare performance against industry benchmarks -- **Text Analysis**: Extract insights from customer feedback -- **Trend Visualization**: Track metrics over time with interactive charts -- **AI Insights**: Get AI-powered recommendations for business growth - ## 🔒 Security - Secure token management @@ -92,10 +91,10 @@ Contributions are welcome! Please feel free to submit a Pull Request. ## 📬 Contact -For questions and support, please open an issue or contact us at support@analysr.app +For questions and support, please open an issue or contact at btk.codedev@gmail.com ---
- Built with ❤️ for growing businesses -
\ No newline at end of file + Built with ❤️ for growing businesses by bktcodedev +
From 05b2597cdcaa39a9b7f18c1f00c9d189b67675cf Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 12:33:17 +0530 Subject: [PATCH 04/24] update cache busting --- package.json | 2 +- vite.config.ts | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f813bc7..45ad78e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "1.0.0", + "version": "1.0.1", "type": "module", "scripts": { "dev": "vite", diff --git a/vite.config.ts b/vite.config.ts index f7ae6da..5a21cf9 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,6 +4,11 @@ import path from 'path'; import { fixRequestBody } from 'http-proxy-middleware'; import { AIRBYTE_API_BASE_URL, AIRBYTE_API_PROXY_URL, GROQ_API_BASE_URL, GROQ_API_PROXY_URL } from './src/config/services/index'; +const cacheBuster = (url: string | undefined) => { + const timestamp = new Date().getTime(); + return `${url}?v=${timestamp}`; +}; + export default defineConfig({ plugins: [react()], server: { @@ -37,5 +42,20 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }, }, + build: { + rollupOptions: { + output: { + // Use a cache-busting filename pattern for output files + entryFileNames: (chunkInfo) => { + return cacheBuster(chunkInfo.name); + }, + chunkFileNames: (chunkInfo) => { + return cacheBuster(chunkInfo.name); + }, + assetFileNames: (assetInfo) => { + return cacheBuster(assetInfo.name); + }, + }, + }, + }, }); - From 5d99461dd1d04f653b35bbf3a2f7d0c75fdaf93f Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 12:41:15 +0530 Subject: [PATCH 05/24] update package.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 66c34b1..f7284d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "analysr", - "version": "0.2.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "analysr", - "version": "0.2.0", + "version": "1.0.1", "dependencies": { "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", From 3d0592f4932180632f6bc90540acc4dea5e8e60d Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 12:47:25 +0530 Subject: [PATCH 06/24] update version and deployment server --- README.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 65db515..42143ce 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for th ## Walkthrough 1) To obtain customer review insights, sync your data to Motherduck with the schema: { "review_text": "string", "stars": "number" } (More schemas will be supported soon). We recommend using Airbyte due to its extensive list of sources and seamless data movement. -2) Visit the Analysr website at (growwithanalysr.web.app) and click on the "Get Started Now" button. +2) Visit the Analysr website at (growwithanalysr.vercel.app) and click on the "Get Started Now" button. 3) Select your business stack and substack; Groq and queries will use this information to fetch insights. 4) Input your Motherduck token and wait for the connection to be established (the time required will depend on your network bandwidth). 5) Select the database and table where your customer reviews or any related reviews exist, and set the data limit. @@ -51,7 +51,7 @@ Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for th - **Visualization**: Recharts - **State Management**: Zustand - **Animations**: Framer Motion -- **Hosting**: Firebase +- **Hosting**: Firebase (Staging), Vercel (Production) - **CI/CD**: GitHub Actions for automated deployment ## 🚀 Getting Started diff --git a/package.json b/package.json index 45ad78e..e358357 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "1.0.1", + "version": "1.0.2", "type": "module", "scripts": { "dev": "vite", From 9785680487657114d34d98f722fedd1fadfb3e4a Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 12:52:09 +0530 Subject: [PATCH 07/24] revert cache buster --- vite.config.ts | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/vite.config.ts b/vite.config.ts index 5a21cf9..4fac2ea 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,12 +2,16 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import path from 'path'; import { fixRequestBody } from 'http-proxy-middleware'; -import { AIRBYTE_API_BASE_URL, AIRBYTE_API_PROXY_URL, GROQ_API_BASE_URL, GROQ_API_PROXY_URL } from './src/config/services/index'; -const cacheBuster = (url: string | undefined) => { - const timestamp = new Date().getTime(); - return `${url}?v=${timestamp}`; -}; +export const AIRBYTE_API_PROXY_URL = "/api/airbyte"; +export const AIRBYTE_API_BASE_URL = "https://api.airbyte.com/v1"; +export const GROQ_API_PROXY_URL = "/api/groq"; +export const GROQ_API_BASE_URL = "https://api.groq.com/v1"; + +// const cacheBuster = (url: string | undefined) => { +// const timestamp = new Date().getTime(); +// return `${url}?v=${timestamp}`; +// }; export default defineConfig({ plugins: [react()], @@ -42,20 +46,20 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }, }, - build: { - rollupOptions: { - output: { - // Use a cache-busting filename pattern for output files - entryFileNames: (chunkInfo) => { - return cacheBuster(chunkInfo.name); - }, - chunkFileNames: (chunkInfo) => { - return cacheBuster(chunkInfo.name); - }, - assetFileNames: (assetInfo) => { - return cacheBuster(assetInfo.name); - }, - }, - }, - }, + // build: { + // rollupOptions: { + // output: { + // // Use a cache-busting filename pattern for output files + // entryFileNames: (chunkInfo) => { + // return cacheBuster(chunkInfo.name); + // }, + // chunkFileNames: (chunkInfo) => { + // return cacheBuster(chunkInfo.name); + // }, + // assetFileNames: (assetInfo) => { + // return cacheBuster(assetInfo.name); + // }, + // }, + // }, + // }, }); From 25f029077c8c654d2d597c1427533acf75f16597 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 13:28:54 +0530 Subject: [PATCH 08/24] update cache busting --- package.json | 2 +- vite.config.ts | 45 ++++++++++++++++++++------------------------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index e358357..f88766d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "1.0.2", + "version": "1.0.3", "type": "module", "scripts": { "dev": "vite", diff --git a/vite.config.ts b/vite.config.ts index 4fac2ea..52cae19 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,16 +2,12 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import path from 'path'; import { fixRequestBody } from 'http-proxy-middleware'; +import { AIRBYTE_API_PROXY_URL, AIRBYTE_API_BASE_URL, GROQ_API_PROXY_URL, GROQ_API_BASE_URL } from './src/config/services/index'; -export const AIRBYTE_API_PROXY_URL = "/api/airbyte"; -export const AIRBYTE_API_BASE_URL = "https://api.airbyte.com/v1"; -export const GROQ_API_PROXY_URL = "/api/groq"; -export const GROQ_API_BASE_URL = "https://api.groq.com/v1"; - -// const cacheBuster = (url: string | undefined) => { -// const timestamp = new Date().getTime(); -// return `${url}?v=${timestamp}`; -// }; +const cacheBuster = (url: string | undefined) => { + const timestamp = new Date().getTime(); + return `${url}?v=${timestamp}`; +}; export default defineConfig({ plugins: [react()], @@ -46,20 +42,19 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }, }, - // build: { - // rollupOptions: { - // output: { - // // Use a cache-busting filename pattern for output files - // entryFileNames: (chunkInfo) => { - // return cacheBuster(chunkInfo.name); - // }, - // chunkFileNames: (chunkInfo) => { - // return cacheBuster(chunkInfo.name); - // }, - // assetFileNames: (assetInfo) => { - // return cacheBuster(assetInfo.name); - // }, - // }, - // }, - // }, + build: { + rollupOptions: { + output: { + entryFileNames: (chunkInfo) => { + return cacheBuster(chunkInfo.name); + }, + chunkFileNames: (chunkInfo) => { + return cacheBuster(chunkInfo.name); + }, + assetFileNames: (assetInfo) => { + return cacheBuster(assetInfo.name); + }, + }, + }, + }, }); From 6715b2190766c815acc9c819f32db3f391de0d45 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 13:54:28 +0530 Subject: [PATCH 09/24] fix: add cache controlling --- src/lib/airbyte/service.ts | 15 ++++++--- src/lib/groq/models.ts | 2 +- vite.config.ts | 68 ++++++++++++++++++++++++-------------- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/lib/airbyte/service.ts b/src/lib/airbyte/service.ts index 74b47e1..0a5e2b9 100644 --- a/src/lib/airbyte/service.ts +++ b/src/lib/airbyte/service.ts @@ -1,6 +1,13 @@ import type { AirbyteConfig, SyncJobResponse } from "./types"; import { AIRBYTE_API_PROXY_URL as API_BASE_URL } from "../../config/services"; +const noCacheHeaders = { + 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate', + 'Pragma': 'no-cache', + 'Expires': '0', + 'X-Request-Timestamp': Date.now().toString(), +}; + export async function checkConnectionStatus( config: AirbyteConfig ): Promise { @@ -9,10 +16,10 @@ export async function checkConnectionStatus( throw new Error("Bearer token is required"); } - const response = await fetch(`${API_BASE_URL}/sources`, { + const response = await fetch(`${API_BASE_URL}/sources?_=${Date.now()}`, { method: "GET", headers: { - 'Cache-Control': 'no-cache', + ...noCacheHeaders, authorization: `Bearer ${config.bearerToken}`, accept: "application/json", }, @@ -39,10 +46,10 @@ export async function triggerJob( } try { - const response = await fetch(`${API_BASE_URL}/jobs`, { + const response = await fetch(`${API_BASE_URL}/jobs?_=${Date.now()}`, { method: "POST", headers: { - 'Cache-Control': 'no-cache', + ...noCacheHeaders, authorization: `Bearer ${config.bearerToken}`, accept: "application/json", "content-type": "application/json", diff --git a/src/lib/groq/models.ts b/src/lib/groq/models.ts index 486ac85..67f8e54 100644 --- a/src/lib/groq/models.ts +++ b/src/lib/groq/models.ts @@ -12,7 +12,7 @@ export const GROQ_MODELS: GroqModel[] = [ export async function fetchAvailableModels(token: string): Promise { try { - const response = await fetch(`${GROQ_API_PROXY_URL}/v1/models`, { + const response = await fetch(`${GROQ_API_PROXY_URL}/models`, { headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', diff --git a/vite.config.ts b/vite.config.ts index 52cae19..1112684 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,12 +2,12 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import path from 'path'; import { fixRequestBody } from 'http-proxy-middleware'; -import { AIRBYTE_API_PROXY_URL, AIRBYTE_API_BASE_URL, GROQ_API_PROXY_URL, GROQ_API_BASE_URL } from './src/config/services/index'; - -const cacheBuster = (url: string | undefined) => { - const timestamp = new Date().getTime(); - return `${url}?v=${timestamp}`; -}; +import { + AIRBYTE_API_PROXY_URL, + AIRBYTE_API_BASE_URL, + GROQ_API_PROXY_URL, + GROQ_API_BASE_URL +} from './src/config/services/index'; export default defineConfig({ plugins: [react()], @@ -17,6 +17,9 @@ export default defineConfig({ headers: { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp', + 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate', + 'Pragma': 'no-cache', + 'Expires': '0', }, proxy: { [AIRBYTE_API_PROXY_URL]: { @@ -24,7 +27,18 @@ export default defineConfig({ changeOrigin: true, rewrite: (path) => path.replace(/^\/api\/airbyte/, ''), configure: (proxy, _options) => { - proxy.on('proxyReq', fixRequestBody); + proxy.on('proxyReq', (proxyReq, req) => { + proxyReq.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); + proxyReq.setHeader('Pragma', 'no-cache'); + proxyReq.setHeader('Expires', '0'); + fixRequestBody(proxyReq, req); + }); + + proxy.on('proxyRes', (proxyRes) => { + proxyRes.headers['cache-control'] = 'no-store, no-cache, must-revalidate, proxy-revalidate'; + proxyRes.headers['pragma'] = 'no-cache'; + proxyRes.headers['expires'] = '0'; + }); }, }, [GROQ_API_PROXY_URL]: { @@ -32,29 +46,35 @@ export default defineConfig({ changeOrigin: true, rewrite: (path) => path.replace(/^\/api\/groq/, ''), configure: (proxy, _options) => { - proxy.on('proxyReq', fixRequestBody); + proxy.on('proxyReq', (proxyReq, req) => { + proxyReq.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); + proxyReq.setHeader('Pragma', 'no-cache'); + proxyReq.setHeader('Expires', '0'); + fixRequestBody(proxyReq, req); + }); + + proxy.on('proxyRes', (proxyRes) => { + proxyRes.headers['cache-control'] = 'no-store, no-cache, must-revalidate, proxy-revalidate'; + proxyRes.headers['pragma'] = 'no-cache'; + proxyRes.headers['expires'] = '0'; + }); }, } }, }, - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - }, - }, build: { + outDir: 'dist', rollupOptions: { output: { - entryFileNames: (chunkInfo) => { - return cacheBuster(chunkInfo.name); - }, - chunkFileNames: (chunkInfo) => { - return cacheBuster(chunkInfo.name); - }, - assetFileNames: (assetInfo) => { - return cacheBuster(assetInfo.name); - }, - }, - }, + entryFileNames: `assets/[name].[hash].js`, + chunkFileNames: `assets/[name].[hash].js`, + assetFileNames: `assets/[name].[hash].[ext]` + } + } }, + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + } }); From 9df33aedfa16c1c974b90b90e741c46987178a1f Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 13:55:00 +0530 Subject: [PATCH 10/24] update version to 1.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f88766d..b67d162 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "1.0.3", + "version": "1.0.4", "type": "module", "scripts": { "dev": "vite", From cec36539330e92f212ba4de2327abefe5dd89e52 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 14:29:02 +0530 Subject: [PATCH 11/24] fix: remove proxy for production --- package.json | 2 +- src/config/services/index.ts | 16 +++- src/lib/airbyte/service.ts | 11 +-- vite.config.ts | 162 +++++++++++++++++++---------------- 4 files changed, 111 insertions(+), 80 deletions(-) diff --git a/package.json b/package.json index b67d162..f8e48be 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "1.0.4", + "version": "1.0.5", "type": "module", "scripts": { "dev": "vite", diff --git a/src/config/services/index.ts b/src/config/services/index.ts index 6604449..a7086c7 100644 --- a/src/config/services/index.ts +++ b/src/config/services/index.ts @@ -1,4 +1,18 @@ export const AIRBYTE_API_PROXY_URL = "/api/airbyte"; export const AIRBYTE_API_BASE_URL = "https://api.airbyte.com/v1"; export const GROQ_API_PROXY_URL = "/api/groq"; -export const GROQ_API_BASE_URL = "https://api.groq.com/v1"; \ No newline at end of file +export const GROQ_API_BASE_URL = "https://api.groq.com/v1"; + +export const getAirbyteApiUrl = () => { + if (import.meta.env.DEV) { + return AIRBYTE_API_PROXY_URL; + } + return AIRBYTE_API_BASE_URL; + }; + + export const getGroqApiUrl = () => { + if (import.meta.env.DEV) { + return GROQ_API_PROXY_URL; + } + return GROQ_API_BASE_URL; + }; diff --git a/src/lib/airbyte/service.ts b/src/lib/airbyte/service.ts index 0a5e2b9..1be237a 100644 --- a/src/lib/airbyte/service.ts +++ b/src/lib/airbyte/service.ts @@ -1,5 +1,5 @@ import type { AirbyteConfig, SyncJobResponse } from "./types"; -import { AIRBYTE_API_PROXY_URL as API_BASE_URL } from "../../config/services"; +import { getAirbyteApiUrl } from "../../config/services"; const noCacheHeaders = { 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate', @@ -14,9 +14,9 @@ export async function checkConnectionStatus( try { if (!config.bearerToken) { throw new Error("Bearer token is required"); - } - - const response = await fetch(`${API_BASE_URL}/sources?_=${Date.now()}`, { + } + const url = getAirbyteApiUrl() + const response = await fetch(`${url}/sources?_=${Date.now()}`, { method: "GET", headers: { ...noCacheHeaders, @@ -46,7 +46,8 @@ export async function triggerJob( } try { - const response = await fetch(`${API_BASE_URL}/jobs?_=${Date.now()}`, { + const url = getAirbyteApiUrl() + const response = await fetch(`${url}/jobs?_=${Date.now()}`, { method: "POST", headers: { ...noCacheHeaders, diff --git a/vite.config.ts b/vite.config.ts index 1112684..80a6fd5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,80 +1,96 @@ -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react'; -import path from 'path'; -import { fixRequestBody } from 'http-proxy-middleware'; -import { - AIRBYTE_API_PROXY_URL, - AIRBYTE_API_BASE_URL, - GROQ_API_PROXY_URL, - GROQ_API_BASE_URL -} from './src/config/services/index'; +import { defineConfig, loadEnv } from 'vite'; +import react from "@vitejs/plugin-react"; +import path from "path"; +import { fixRequestBody } from "http-proxy-middleware"; +import { + AIRBYTE_API_PROXY_URL as AB_PROX, + AIRBYTE_API_BASE_URL as AB_BASE, + GROQ_API_PROXY_URL as GROQ_PROX, + GROQ_API_BASE_URL as GROQ_BASE, +} from "./src/config/services/index"; -export default defineConfig({ - plugins: [react()], - server: { - port: 5173, - host: true, - headers: { - 'Cross-Origin-Opener-Policy': 'same-origin', - 'Cross-Origin-Embedder-Policy': 'require-corp', - 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate', - 'Pragma': 'no-cache', - 'Expires': '0', - }, - proxy: { - [AIRBYTE_API_PROXY_URL]: { - target: AIRBYTE_API_BASE_URL, - changeOrigin: true, - rewrite: (path) => path.replace(/^\/api\/airbyte/, ''), - configure: (proxy, _options) => { - proxy.on('proxyReq', (proxyReq, req) => { - proxyReq.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); - proxyReq.setHeader('Pragma', 'no-cache'); - proxyReq.setHeader('Expires', '0'); - fixRequestBody(proxyReq, req); - }); - - proxy.on('proxyRes', (proxyRes) => { - proxyRes.headers['cache-control'] = 'no-store, no-cache, must-revalidate, proxy-revalidate'; - proxyRes.headers['pragma'] = 'no-cache'; - proxyRes.headers['expires'] = '0'; - }); +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ""); + const AIRBYTE_API_PROXY_URL = AB_PROX; + const AIRBYTE_API_BASE_URL = env.VITE_AIRBYTE_API_BASE_URL || AB_BASE; + const GROQ_API_PROXY_URL = GROQ_PROX; + const GROQ_API_BASE_URL = env.VITE_GROQ_API_BASE_URL || GROQ_BASE; + return { + plugins: [react()], + server: { + port: 5173, + host: true, + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + "Cache-Control": + "no-store, no-cache, must-revalidate, proxy-revalidate", + Pragma: "no-cache", + Expires: "0", + }, + proxy: { + [AIRBYTE_API_PROXY_URL]: { + target: AIRBYTE_API_BASE_URL, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api\/airbyte/, ""), + configure: (proxy, _options) => { + proxy.on("proxyReq", (proxyReq, req) => { + proxyReq.setHeader( + "Cache-Control", + "no-store, no-cache, must-revalidate, proxy-revalidate" + ); + proxyReq.setHeader("Pragma", "no-cache"); + proxyReq.setHeader("Expires", "0"); + fixRequestBody(proxyReq, req); + }); + + proxy.on("proxyRes", (proxyRes) => { + proxyRes.headers["cache-control"] = + "no-store, no-cache, must-revalidate, proxy-revalidate"; + proxyRes.headers["pragma"] = "no-cache"; + proxyRes.headers["expires"] = "0"; + }); + }, + }, + [GROQ_API_PROXY_URL]: { + target: GROQ_API_BASE_URL, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api\/groq/, ""), + configure: (proxy, _options) => { + proxy.on("proxyReq", (proxyReq, req) => { + proxyReq.setHeader( + "Cache-Control", + "no-store, no-cache, must-revalidate, proxy-revalidate" + ); + proxyReq.setHeader("Pragma", "no-cache"); + proxyReq.setHeader("Expires", "0"); + fixRequestBody(proxyReq, req); + }); + + proxy.on("proxyRes", (proxyRes) => { + proxyRes.headers["cache-control"] = + "no-store, no-cache, must-revalidate, proxy-revalidate"; + proxyRes.headers["pragma"] = "no-cache"; + proxyRes.headers["expires"] = "0"; + }); + }, }, }, - [GROQ_API_PROXY_URL]: { - target: GROQ_API_BASE_URL, - changeOrigin: true, - rewrite: (path) => path.replace(/^\/api\/groq/, ''), - configure: (proxy, _options) => { - proxy.on('proxyReq', (proxyReq, req) => { - proxyReq.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); - proxyReq.setHeader('Pragma', 'no-cache'); - proxyReq.setHeader('Expires', '0'); - fixRequestBody(proxyReq, req); - }); - - proxy.on('proxyRes', (proxyRes) => { - proxyRes.headers['cache-control'] = 'no-store, no-cache, must-revalidate, proxy-revalidate'; - proxyRes.headers['pragma'] = 'no-cache'; - proxyRes.headers['expires'] = '0'; - }); + }, + build: { + outDir: "dist", + rollupOptions: { + output: { + entryFileNames: `assets/[name].[hash].js`, + chunkFileNames: `assets/[name].[hash].js`, + assetFileNames: `assets/[name].[hash].[ext]`, }, - } + }, }, - }, - build: { - outDir: 'dist', - rollupOptions: { - output: { - entryFileNames: `assets/[name].[hash].js`, - chunkFileNames: `assets/[name].[hash].js`, - assetFileNames: `assets/[name].[hash].[ext]` - } - } - }, - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, }, - } + }; }); From 5760a09322eea45e5f2bf66e63aee242beaad526 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 14:33:25 +0530 Subject: [PATCH 12/24] fix vite server --- package.json | 2 +- vite.config.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f8e48be..bb6dc7c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "1.0.5", + "version": "1.0.6", "type": "module", "scripts": { "dev": "vite", diff --git a/vite.config.ts b/vite.config.ts index 80a6fd5..a241c6d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,11 +10,11 @@ import { } from "./src/config/services/index"; export default defineConfig(({ mode }) => { - const env = loadEnv(mode, process.cwd(), ""); + loadEnv(mode, process.cwd(), ""); const AIRBYTE_API_PROXY_URL = AB_PROX; - const AIRBYTE_API_BASE_URL = env.VITE_AIRBYTE_API_BASE_URL || AB_BASE; + const AIRBYTE_API_BASE_URL = AB_BASE; const GROQ_API_PROXY_URL = GROQ_PROX; - const GROQ_API_BASE_URL = env.VITE_GROQ_API_BASE_URL || GROQ_BASE; + const GROQ_API_BASE_URL = GROQ_BASE; return { plugins: [react()], server: { From aad2d38ef76d9b011dd4ce81ae89dbc198901ce6 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 14:36:03 +0530 Subject: [PATCH 13/24] fix: revert back airbyte url to url proxy server --- package.json | 2 +- src/config/services/index.ts | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index bb6dc7c..76cbb62 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "1.0.6", + "version": "1.0.7", "type": "module", "scripts": { "dev": "vite", diff --git a/src/config/services/index.ts b/src/config/services/index.ts index a7086c7..8cab0d1 100644 --- a/src/config/services/index.ts +++ b/src/config/services/index.ts @@ -4,15 +4,19 @@ export const GROQ_API_PROXY_URL = "/api/groq"; export const GROQ_API_BASE_URL = "https://api.groq.com/v1"; export const getAirbyteApiUrl = () => { - if (import.meta.env.DEV) { - return AIRBYTE_API_PROXY_URL; - } - return AIRBYTE_API_BASE_URL; + // if (import.meta.env.DEV) { + // return AIRBYTE_API_PROXY_URL; + // } + // return AIRBYTE_API_BASE_URL; + + return AIRBYTE_API_PROXY_URL; }; export const getGroqApiUrl = () => { - if (import.meta.env.DEV) { - return GROQ_API_PROXY_URL; - } - return GROQ_API_BASE_URL; + // if (import.meta.env.DEV) { + // return GROQ_API_PROXY_URL; + // } + // return GROQ_API_BASE_URL; + + return GROQ_API_PROXY_URL; }; From abd3951968c1cc4db370cf9262ad2c82d951b8e5 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 15:15:25 +0530 Subject: [PATCH 14/24] update proxy to supabase --- package-lock.json | 293 ++++++++++++++++++++++++++++++++++- package.json | 3 +- src/config/services/index.ts | 4 +- 3 files changed, 295 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f7284d2..0b114ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "analysr", - "version": "1.0.1", + "version": "1.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "analysr", - "version": "1.0.1", + "version": "1.0.7", "dependencies": { "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", @@ -34,6 +34,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", "postcss": "^8.4.35", + "supabase": "^2.2.1", "tailwindcss": "^3.4.1", "typescript": "^5.3.3", "vite": "^5.1.4" @@ -1025,6 +1026,19 @@ "node": ">=12" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -2253,6 +2267,16 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/agentkeepalive": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", @@ -2439,6 +2463,23 @@ "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" }, + "node_modules/bin-links": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-5.0.0.tgz", + "integrity": "sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA==", + "dev": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -2683,6 +2724,16 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2691,6 +2742,16 @@ "node": ">=6" } }, + "node_modules/cmd-shim": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-7.0.0.tgz", + "integrity": "sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2956,6 +3017,16 @@ "node": ">=12" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -3469,6 +3540,40 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fetch-blob/node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3617,6 +3722,19 @@ "node": ">= 12.20" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -3890,6 +4008,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -4315,6 +4447,52 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz", + "integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/minizlib/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/motion-dom": { "version": "11.14.3", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.14.3.tgz", @@ -4426,6 +4604,16 @@ "node": ">=0.10.0" } }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4793,6 +4981,16 @@ "node": ">= 0.8.0" } }, + "node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4942,6 +5140,16 @@ "pify": "^2.3.0" } }, + "node_modules/read-cmd-shim": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-5.0.0.tgz", + "integrity": "sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5335,6 +5543,45 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/supabase": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.2.1.tgz", + "integrity": "sha512-uTu8f4kT9wE3EEQTAJAWFlHyu8ymauFxWEz2FDGIQ2MzlD1Xb1NCHtsj/xvmJWqrGI1jnBYcPuatZVTHj1jR1g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bin-links": "^5.0.0", + "https-proxy-agent": "^7.0.2", + "node-fetch": "^3.3.2", + "tar": "7.4.3" + }, + "bin": { + "supabase": "bin/supabase" + }, + "engines": { + "npm": ">=8" + } + }, + "node_modules/supabase/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -5416,6 +5663,34 @@ "node": ">=14.0.0" } }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5853,6 +6128,20 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/write-file-atomic": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz", + "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index 76cbb62..5dae79e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "1.0.7", + "version": "1.0.8", "type": "module", "scripts": { "dev": "vite", @@ -36,6 +36,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", "postcss": "^8.4.35", + "supabase": "^2.2.1", "tailwindcss": "^3.4.1", "typescript": "^5.3.3", "vite": "^5.1.4" diff --git a/src/config/services/index.ts b/src/config/services/index.ts index 8cab0d1..acde850 100644 --- a/src/config/services/index.ts +++ b/src/config/services/index.ts @@ -1,6 +1,6 @@ -export const AIRBYTE_API_PROXY_URL = "/api/airbyte"; +export const AIRBYTE_API_PROXY_URL = "https://lejxudxaxuqfkhtgddoe.supabase.co/functions/v1/apiProxy/airbyte"; export const AIRBYTE_API_BASE_URL = "https://api.airbyte.com/v1"; -export const GROQ_API_PROXY_URL = "/api/groq"; +export const GROQ_API_PROXY_URL = "https://lejxudxaxuqfkhtgddoe.supabase.co/functions/v1/apiProxy/groq"; export const GROQ_API_BASE_URL = "https://api.groq.com/v1"; export const getAirbyteApiUrl = () => { From 2d0052cc4c56725082992a7d1b57be3d1675acbf Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 16:07:21 +0530 Subject: [PATCH 15/24] fix: update vite config --- README.md | 10 +++++++++- src/config/services/index.ts | 26 ++++++++++++-------------- vite.config.ts | 4 ++-- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 42143ce..3f81906 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,6 @@ Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for th 10) Voilà! Your dashboard will be ready, featuring all the capabilities of Analysr to support your next big step! - ## ✨ Features - **Aspect Analysis:** Gain insights into different aspects of customer feedback. @@ -54,6 +53,15 @@ Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for th - **Hosting**: Firebase (Staging), Vercel (Production) - **CI/CD**: GitHub Actions for automated deployment + +## Future roadmap + +- **Microservice for generating queries**: Currently all queries for analytics are highly coupled with code, seperation of concerns to microservice + - [*] Create mock express server and deployed as supabase functions + - [] Separate DuckDB queries for as an api call + - [] Enhance microservice with GPT Wrapper + - [] Enhance business insights from groq: Currently it hallucinates as the mixtral model is not powerful (Requires funding) + ## 🚀 Getting Started 1. Clone the repository: diff --git a/src/config/services/index.ts b/src/config/services/index.ts index acde850..050d5da 100644 --- a/src/config/services/index.ts +++ b/src/config/services/index.ts @@ -1,22 +1,20 @@ -export const AIRBYTE_API_PROXY_URL = "https://lejxudxaxuqfkhtgddoe.supabase.co/functions/v1/apiProxy/airbyte"; +export const AIRBYTE_SUPABASE_PROXY_URL = "https://lejxudxaxuqfkhtgddoe.supabase.co/functions/v1/apiProxy/airbyte"; +export const AIRBYTE_LOCAL_PROXY_URL = "/api/airbyte"; export const AIRBYTE_API_BASE_URL = "https://api.airbyte.com/v1"; -export const GROQ_API_PROXY_URL = "https://lejxudxaxuqfkhtgddoe.supabase.co/functions/v1/apiProxy/groq"; +export const GROQ_SUPABASE_PROXY_URL = "https://lejxudxaxuqfkhtgddoe.supabase.co/functions/v1/apiProxy/groq"; +export const GROQ_LOCAL_PROXY_URL = "/api/groq"; export const GROQ_API_BASE_URL = "https://api.groq.com/v1"; export const getAirbyteApiUrl = () => { - // if (import.meta.env.DEV) { - // return AIRBYTE_API_PROXY_URL; - // } - // return AIRBYTE_API_BASE_URL; - - return AIRBYTE_API_PROXY_URL; + if (import.meta.env.DEV) { + return AIRBYTE_LOCAL_PROXY_URL; + } + return AIRBYTE_SUPABASE_PROXY_URL; }; export const getGroqApiUrl = () => { - // if (import.meta.env.DEV) { - // return GROQ_API_PROXY_URL; - // } - // return GROQ_API_BASE_URL; - - return GROQ_API_PROXY_URL; + if (import.meta.env.DEV) { + return GROQ_LOCAL_PROXY_URL; + } + return GROQ_SUPABASE_PROXY_URL; }; diff --git a/vite.config.ts b/vite.config.ts index a241c6d..6c2cd53 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,9 +3,9 @@ import react from "@vitejs/plugin-react"; import path from "path"; import { fixRequestBody } from "http-proxy-middleware"; import { - AIRBYTE_API_PROXY_URL as AB_PROX, + AIRBYTE_LOCAL_PROXY_URL as AB_PROX, AIRBYTE_API_BASE_URL as AB_BASE, - GROQ_API_PROXY_URL as GROQ_PROX, + GROQ_LOCAL_PROXY_URL as GROQ_PROX, GROQ_API_BASE_URL as GROQ_BASE, } from "./src/config/services/index"; From a1774f6fc4e30bc90d04152f0ba639733b716e33 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 16:09:35 +0530 Subject: [PATCH 16/24] update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5dae79e..5cc2e7e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "1.0.8", + "version": "1.0.9", "type": "module", "scripts": { "dev": "vite", From 901702c6f5074fcfeebbd6fdb625bbefa80d16ad Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 16:11:39 +0530 Subject: [PATCH 17/24] update url path --- package.json | 2 +- src/lib/groq/models.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5cc2e7e..93022f8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "analysr", "private": true, - "version": "1.0.9", + "version": "1.0.10", "type": "module", "scripts": { "dev": "vite", diff --git a/src/lib/groq/models.ts b/src/lib/groq/models.ts index 67f8e54..1b10472 100644 --- a/src/lib/groq/models.ts +++ b/src/lib/groq/models.ts @@ -1,4 +1,4 @@ -import { GROQ_API_PROXY_URL } from '../../config/services'; +import { getGroqApiUrl } from '../../config/services'; import type { GroqModel } from './types'; export const GROQ_MODELS: GroqModel[] = [ @@ -12,7 +12,8 @@ export const GROQ_MODELS: GroqModel[] = [ export async function fetchAvailableModels(token: string): Promise { try { - const response = await fetch(`${GROQ_API_PROXY_URL}/models`, { + const url = getGroqApiUrl() + const response = await fetch(`${url}/models`, { headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', From ef2514af6dc56cbcb3232e88b1e4bd20774bc2f6 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 16:16:10 +0530 Subject: [PATCH 18/24] update readme and cors for vercel --- .eslintrc.cjs | 18 +++++++++--------- README.md | 4 ++-- eslint.config.js | 22 +++++++++++----------- firebase.json | 8 ++------ vercel.json | 17 +++++++++++++++++ 5 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 vercel.json diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 36f7e57..6e8698b 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -2,17 +2,17 @@ module.exports = { root: true, env: { browser: true, es2020: true }, extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], rules: { - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], }, -} \ No newline at end of file +}; diff --git a/README.md b/README.md index 3f81906..5cae9f2 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for th ## Walkthrough 1) To obtain customer review insights, sync your data to Motherduck with the schema: { "review_text": "string", "stars": "number" } (More schemas will be supported soon). We recommend using Airbyte due to its extensive list of sources and seamless data movement. -2) Visit the Analysr website at (growwithanalysr.vercel.app) and click on the "Get Started Now" button. +2) Visit the Analysr website at (growwithanalysr.web.app) and click on the "Get Started Now" button. 3) Select your business stack and substack; Groq and queries will use this information to fetch insights. 4) Input your Motherduck token and wait for the connection to be established (the time required will depend on your network bandwidth). 5) Select the database and table where your customer reviews or any related reviews exist, and set the data limit. @@ -50,7 +50,7 @@ Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for th - **Visualization**: Recharts - **State Management**: Zustand - **Animations**: Framer Motion -- **Hosting**: Firebase (Staging), Vercel (Production) +- **Hosting**: Firebase (Production), Vercel (Experiment) - **CI/CD**: GitHub Actions for automated deployment diff --git a/eslint.config.js b/eslint.config.js index 82c2e20..0bbf074 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,26 +1,26 @@ -import js from '@eslint/js'; -import globals from 'globals'; -import reactHooks from 'eslint-plugin-react-hooks'; -import reactRefresh from 'eslint-plugin-react-refresh'; -import tseslint from 'typescript-eslint'; +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; export default tseslint.config( - { ignores: ['dist'] }, + { ignores: ["dist"] }, { extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], + files: ["**/*.{ts,tsx}"], languageOptions: { ecmaVersion: 2020, globals: globals.browser, }, plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, + "react-hooks": reactHooks, + "react-refresh": reactRefresh, }, rules: { ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], }, diff --git a/firebase.json b/firebase.json index 97aa404..0a0ba89 100644 --- a/firebase.json +++ b/firebase.json @@ -1,11 +1,7 @@ { "hosting": { "public": "dist", - "ignore": [ - "firebase.json", - "**/.*", - "**/node_modules/**" - ], + "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], "rewrites": [ { "source": "**", @@ -28,4 +24,4 @@ } ] } -} \ No newline at end of file +} diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..4a80ded --- /dev/null +++ b/vercel.json @@ -0,0 +1,17 @@ +{ + "headers": [ + { + "source": "**", + "headers": [ + { + "key": "Cross-Origin-Opener-Policy", + "value": "same-origin" + }, + { + "key": "Cross-Origin-Embedder-Policy", + "value": "require-corp" + } + ] + } + ] +} From 0de08f0e868f2ea89e950e37a521adab252f2dde Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 16:19:09 +0530 Subject: [PATCH 19/24] update readme and cors for vercel --- .eslintrc.cjs | 18 +++++++++--------- README.md | 4 ++-- eslint.config.js | 22 +++++++++++----------- firebase.json | 8 ++------ vercel.json | 17 +++++++++++++++++ 5 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 vercel.json diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 36f7e57..6e8698b 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -2,17 +2,17 @@ module.exports = { root: true, env: { browser: true, es2020: true }, extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], rules: { - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], }, -} \ No newline at end of file +}; diff --git a/README.md b/README.md index 3f81906..5cae9f2 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for th ## Walkthrough 1) To obtain customer review insights, sync your data to Motherduck with the schema: { "review_text": "string", "stars": "number" } (More schemas will be supported soon). We recommend using Airbyte due to its extensive list of sources and seamless data movement. -2) Visit the Analysr website at (growwithanalysr.vercel.app) and click on the "Get Started Now" button. +2) Visit the Analysr website at (growwithanalysr.web.app) and click on the "Get Started Now" button. 3) Select your business stack and substack; Groq and queries will use this information to fetch insights. 4) Input your Motherduck token and wait for the connection to be established (the time required will depend on your network bandwidth). 5) Select the database and table where your customer reviews or any related reviews exist, and set the data limit. @@ -50,7 +50,7 @@ Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for th - **Visualization**: Recharts - **State Management**: Zustand - **Animations**: Framer Motion -- **Hosting**: Firebase (Staging), Vercel (Production) +- **Hosting**: Firebase (Production), Vercel (Experiment) - **CI/CD**: GitHub Actions for automated deployment diff --git a/eslint.config.js b/eslint.config.js index 82c2e20..0bbf074 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,26 +1,26 @@ -import js from '@eslint/js'; -import globals from 'globals'; -import reactHooks from 'eslint-plugin-react-hooks'; -import reactRefresh from 'eslint-plugin-react-refresh'; -import tseslint from 'typescript-eslint'; +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; export default tseslint.config( - { ignores: ['dist'] }, + { ignores: ["dist"] }, { extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], + files: ["**/*.{ts,tsx}"], languageOptions: { ecmaVersion: 2020, globals: globals.browser, }, plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, + "react-hooks": reactHooks, + "react-refresh": reactRefresh, }, rules: { ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], }, diff --git a/firebase.json b/firebase.json index 97aa404..0a0ba89 100644 --- a/firebase.json +++ b/firebase.json @@ -1,11 +1,7 @@ { "hosting": { "public": "dist", - "ignore": [ - "firebase.json", - "**/.*", - "**/node_modules/**" - ], + "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], "rewrites": [ { "source": "**", @@ -28,4 +24,4 @@ } ] } -} \ No newline at end of file +} diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..4a80ded --- /dev/null +++ b/vercel.json @@ -0,0 +1,17 @@ +{ + "headers": [ + { + "source": "**", + "headers": [ + { + "key": "Cross-Origin-Opener-Policy", + "value": "same-origin" + }, + { + "key": "Cross-Origin-Embedder-Policy", + "value": "require-corp" + } + ] + } + ] +} From eef2fd3c5ceb2943cae8523685b7a35002b1d1a5 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 16:21:04 +0530 Subject: [PATCH 20/24] update vercel.json --- vercel.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vercel.json b/vercel.json index 4a80ded..944cd68 100644 --- a/vercel.json +++ b/vercel.json @@ -1,7 +1,7 @@ { "headers": [ { - "source": "**", + "source": "/(.*)", "headers": [ { "key": "Cross-Origin-Opener-Policy", From 2efa4ebe52c19a54114b619d38828bb14726c99b Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 16:50:25 +0530 Subject: [PATCH 21/24] Update README.md --- README.md | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 5cae9f2..fd005d7 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,8 @@ # Analysr - Review Analytics Platform 📊 - -
- Analysr Banner +![image](https://github.com/user-attachments/assets/1b26013b-25e6-4b70-a725-8a42faa91336) -

- Transform your business with Analysr -

-
+Transform your business with Analysr ## 1️⃣ One Liner This is my submission for Airbyte-Motherduck Hackathon - December 2024 - January 2025 @@ -21,17 +16,32 @@ Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for th ## Walkthrough -1) To obtain customer review insights, sync your data to Motherduck with the schema: { "review_text": "string", "stars": "number" } (More schemas will be supported soon). We recommend using Airbyte due to its extensive list of sources and seamless data movement. -2) Visit the Analysr website at (growwithanalysr.web.app) and click on the "Get Started Now" button. +1) To obtain customer review insights, sync your data to Motherduck with the schema: { "review_text": "string", "stars": "number" } (More schemas will be supported soon). We recommend using Airbyte due to its extensive list of sources and seamless data movement. ![image](https://github.com/user-attachments/assets/415aece5-6594-4649-8d84-ec2fa1707988) +![image](https://github.com/user-attachments/assets/00bf63f5-952f-491a-9ffd-0241d2e2bfd2) +2) Visit the Analysr website at (growwithanalysr.web.app) and click on the "Get Started Now" button for onboarding. +![image](https://github.com/user-attachments/assets/95da4b69-29bb-4c88-9433-19865bc72093) 3) Select your business stack and substack; Groq and queries will use this information to fetch insights. +![image](https://github.com/user-attachments/assets/160c95bb-bad3-4c27-b5af-7fe651f2313c) 4) Input your Motherduck token and wait for the connection to be established (the time required will depend on your network bandwidth). +![image](https://github.com/user-attachments/assets/18d35b48-37c4-4348-8ea2-8c501a14f00a) 5) Select the database and table where your customer reviews or any related reviews exist, and set the data limit. +![image](https://github.com/user-attachments/assets/e88e07d8-1861-4f12-9dc9-672b45776509) 6) Input your Groq token (recommended) to obtain AI-based insights. +![image](https://github.com/user-attachments/assets/19178890-f24b-4d1c-ad07-f343e06c79c6) 7) Optionally provide your Airbyte bearer token (from the cloud.airbyte.com settings page) and connection ID (from the connections tab URL) to trigger a sync for updating your Motherduck table. -8) Finally, input your area of interest for insights, such as customer satisfaction, and click "Continue to Dashboard." +![image](https://github.com/user-attachments/assets/042ee2bf-8ff1-4ba3-b32c-cd720e52fb8e) +![image](https://github.com/user-attachments/assets/9f3ae847-28e2-4a93-b8a5-354b87835962) +8) Finally, input your area of interest for insights, such as customer satisfaction, and click "Continue to Dashboard."![image](https://github.com/user-attachments/assets/3c938fa2-a862-4ba6-b06e-b67bb139e71f) 9) Wait a few seconds until all queries are executed and visualized. -10) Voilà! Your dashboard will be ready, featuring all the capabilities of Analysr to support your next big step! +![image](https://github.com/user-attachments/assets/cf22aa51-cdb2-4e3f-99d6-ef93bf8f8c45) +10) Voilà! Your dashboard will be ready, featuring all Analysr's capabilities to support your next big step! +![image](https://github.com/user-attachments/assets/1ae1427d-c315-4e02-ac75-158e3cb14d61) +Need dataset and example method to test? +1. Hugging face dataset URL which I used, https://huggingface.co/datasets/Yelp/yelp_review_full +2. Import it to motherduck via airbyte (Set huggingface as source and motherduck as destination) +3. Get Groq token at, https://console.groq.com/keys +4. Click on continue to dashboard! That's it. Please try yourself, its fun! ## ✨ Features @@ -51,16 +61,16 @@ Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for th - **State Management**: Zustand - **Animations**: Framer Motion - **Hosting**: Firebase (Production), Vercel (Experiment) +- **Proxy**: Supabase edge functions - **CI/CD**: GitHub Actions for automated deployment - ## Future roadmap - **Microservice for generating queries**: Currently all queries for analytics are highly coupled with code, seperation of concerns to microservice - - [*] Create mock express server and deployed as supabase functions - - [] Separate DuckDB queries for as an api call - - [] Enhance microservice with GPT Wrapper - - [] Enhance business insights from groq: Currently it hallucinates as the mixtral model is not powerful (Requires funding) + - [x] Create mock express server and deployed as supabase functions + - [ ] Separate DuckDB queries for as an api call + - [ ] Enhance microservice with GPT Wrapper + - [ ] Enhance business insights from Groq: Currently it hallucinates as the mixtral model is not powerful (Requires funding) ## 🚀 Getting Started @@ -91,7 +101,7 @@ npm run dev ## 📄 License -MIT License - feel free to use this project for your own purposes! +MIT License - feel free to use this project for your purposes! ## 🤝 Contributing From 4f1109578ca37897265d2a2b94aa5e50fab310d7 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 16:54:13 +0530 Subject: [PATCH 22/24] Update README.md --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fd005d7..f59cbbc 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,16 @@ Transform your business with Analysr -## 1️⃣ One Liner +## ⚡ One Liner This is my submission for Airbyte-Motherduck Hackathon - December 2024 - January 2025 -For all you speedy folks out there, here’s the scoop: +For all you speedy folks out there, here’s the summary: - **1.0.0** - With your customer reviews in motherduck, along with your chosen business stack and areas of interest, Analysr is ready to dish out some insightful analytics. And just to sweeten the deal, Groq has stepped in to help you navigate all your growth phases—because who doesn’t want a sidekick in business? + - With your customer reviews in Motherduck, along with your chosen business stack and areas of interest, Analysr is ready to dish out some insightful analytics. To sweeten the deal, Groq is also integrated to help you navigate all your growth phases. + - Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for those feelings), Advanced Text Analysis, Groq Business Analytics, Keyphrase Analysis, and a handy Competitor Comparison. -Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for those feelings), Advanced Text Analysis, Groq Business Analytics, Keyphrase Analysis, and a handy Competitor Comparison. - -## Walkthrough +## 🚶Walkthrough 1) To obtain customer review insights, sync your data to Motherduck with the schema: { "review_text": "string", "stars": "number" } (More schemas will be supported soon). We recommend using Airbyte due to its extensive list of sources and seamless data movement. ![image](https://github.com/user-attachments/assets/415aece5-6594-4649-8d84-ec2fa1707988) ![image](https://github.com/user-attachments/assets/00bf63f5-952f-491a-9ffd-0241d2e2bfd2) From e6dbae44299fca10d0e2980dfe71a0d2f77fa5fc Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 16:56:22 +0530 Subject: [PATCH 23/24] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f59cbbc..558c485 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ For all you speedy folks out there, here’s the summary: - With your customer reviews in Motherduck, along with your chosen business stack and areas of interest, Analysr is ready to dish out some insightful analytics. To sweeten the deal, Groq is also integrated to help you navigate all your growth phases. - Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for those feelings), Advanced Text Analysis, Groq Business Analytics, Keyphrase Analysis, and a handy Competitor Comparison. +## Architecture +![image](https://github.com/user-attachments/assets/0abe96f6-414a-42d2-aa0d-d0950a7da194) + + ## 🚶Walkthrough 1) To obtain customer review insights, sync your data to Motherduck with the schema: { "review_text": "string", "stars": "number" } (More schemas will be supported soon). We recommend using Airbyte due to its extensive list of sources and seamless data movement. ![image](https://github.com/user-attachments/assets/415aece5-6594-4649-8d84-ec2fa1707988) From 1d02e3018af419ba910ab808a4ae2b46dde4c272 Mon Sep 17 00:00:00 2001 From: btkcodedev Date: Fri, 10 Jan 2025 16:57:15 +0530 Subject: [PATCH 24/24] Update README.md emojis --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 558c485..f19ff4c 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ For all you speedy folks out there, here’s the summary: - With your customer reviews in Motherduck, along with your chosen business stack and areas of interest, Analysr is ready to dish out some insightful analytics. To sweeten the deal, Groq is also integrated to help you navigate all your growth phases. - Your analytics lineup features Aspect Analysis, a Word Sentiment Heatmap (for those feelings), Advanced Text Analysis, Groq Business Analytics, Keyphrase Analysis, and a handy Competitor Comparison. -## Architecture +## 🏗️ Architecture ![image](https://github.com/user-attachments/assets/0abe96f6-414a-42d2-aa0d-d0950a7da194)