Upload 16 files
Browse files
App.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import React, { useState, useRef, useEffect, Suspense } from 'react';
|
| 2 |
import { Header } from './components/Header';
|
| 3 |
import { ImageUploader } from './components/ImageUploader';
|
|
@@ -41,9 +42,9 @@ const Toast = ({ msg, onClose }: { msg: string, onClose: () => void }) => (
|
|
| 41 |
const App: React.FC = () => {
|
| 42 |
// --- ZUSTAND STORES ---
|
| 43 |
const {
|
| 44 |
-
currentUser, allUsers, leads, feedback,
|
| 45 |
login, register, logout, updateUser, resetPassword, addPoints,
|
| 46 |
-
addLead, updateLeadStatus, deleteLead, addFeedback
|
| 47 |
} = useUserStore();
|
| 48 |
|
| 49 |
const {
|
|
@@ -218,7 +219,7 @@ const App: React.FC = () => {
|
|
| 218 |
updateUser(currentUser.id, { styleStats: stats });
|
| 219 |
|
| 220 |
// Log generation event
|
| 221 |
-
|
| 222 |
styleId,
|
| 223 |
styleName: (TRANSLATIONS[language].styles as any)[styleId] || styleId,
|
| 224 |
userId: currentUser.id,
|
|
@@ -617,6 +618,7 @@ const App: React.FC = () => {
|
|
| 617 |
onUpdateUser={updateUser}
|
| 618 |
showToast={showToast}
|
| 619 |
onAdminLoginSuccess={handleAdminLoginSuccess}
|
|
|
|
| 620 |
/>
|
| 621 |
</Suspense>
|
| 622 |
|
|
@@ -1059,4 +1061,4 @@ const App: React.FC = () => {
|
|
| 1059 |
);
|
| 1060 |
};
|
| 1061 |
|
| 1062 |
-
export default App;
|
|
|
|
| 1 |
+
|
| 2 |
import React, { useState, useRef, useEffect, Suspense } from 'react';
|
| 3 |
import { Header } from './components/Header';
|
| 4 |
import { ImageUploader } from './components/ImageUploader';
|
|
|
|
| 42 |
const App: React.FC = () => {
|
| 43 |
// --- ZUSTAND STORES ---
|
| 44 |
const {
|
| 45 |
+
currentUser, allUsers, leads, feedback, logs,
|
| 46 |
login, register, logout, updateUser, resetPassword, addPoints,
|
| 47 |
+
addLead, updateLeadStatus, deleteLead, addFeedback, addLog
|
| 48 |
} = useUserStore();
|
| 49 |
|
| 50 |
const {
|
|
|
|
| 219 |
updateUser(currentUser.id, { styleStats: stats });
|
| 220 |
|
| 221 |
// Log generation event
|
| 222 |
+
addLog({
|
| 223 |
styleId,
|
| 224 |
styleName: (TRANSLATIONS[language].styles as any)[styleId] || styleId,
|
| 225 |
userId: currentUser.id,
|
|
|
|
| 618 |
onUpdateUser={updateUser}
|
| 619 |
showToast={showToast}
|
| 620 |
onAdminLoginSuccess={handleAdminLoginSuccess}
|
| 621 |
+
logs={logs}
|
| 622 |
/>
|
| 623 |
</Suspense>
|
| 624 |
|
|
|
|
| 1061 |
);
|
| 1062 |
};
|
| 1063 |
|
| 1064 |
+
export default App;
|
index.html
CHANGED
|
@@ -15,17 +15,17 @@
|
|
| 15 |
<script type="importmap">
|
| 16 |
{
|
| 17 |
"imports": {
|
| 18 |
-
"react/": "https://aistudiocdn.com/react@^19.2.1/",
|
| 19 |
"react": "https://aistudiocdn.com/react@^19.2.1",
|
| 20 |
-
"zustand/": "https://aistudiocdn.com/zustand@^5.0.9/",
|
| 21 |
-
"zustand": "https://aistudiocdn.com/zustand@^5.0.9",
|
| 22 |
-
"vite": "https://aistudiocdn.com/vite@^7.2.6",
|
| 23 |
-
"@google/genai": "https://aistudiocdn.com/@google/genai@^1.31.0",
|
| 24 |
-
"@vitejs/plugin-react": "https://aistudiocdn.com/@vitejs/plugin-react@^5.1.1",
|
| 25 |
"react-dom/": "https://aistudiocdn.com/react-dom@^19.2.1/",
|
|
|
|
|
|
|
| 26 |
"lucide-react": "https://aistudiocdn.com/lucide-react@^0.556.0",
|
|
|
|
| 27 |
"canvas-confetti": "https://aistudiocdn.com/canvas-confetti@^1.9.4",
|
| 28 |
-
"
|
|
|
|
|
|
|
|
|
|
| 29 |
}
|
| 30 |
}
|
| 31 |
</script>
|
|
|
|
| 15 |
<script type="importmap">
|
| 16 |
{
|
| 17 |
"imports": {
|
|
|
|
| 18 |
"react": "https://aistudiocdn.com/react@^19.2.1",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
"react-dom/": "https://aistudiocdn.com/react-dom@^19.2.1/",
|
| 20 |
+
"react/": "https://aistudiocdn.com/react@^19.2.1/",
|
| 21 |
+
"@google/genai": "https://aistudiocdn.com/@google/genai@^1.31.0",
|
| 22 |
"lucide-react": "https://aistudiocdn.com/lucide-react@^0.556.0",
|
| 23 |
+
"jszip": "https://aistudiocdn.com/jszip@^3.10.1",
|
| 24 |
"canvas-confetti": "https://aistudiocdn.com/canvas-confetti@^1.9.4",
|
| 25 |
+
"zustand": "https://aistudiocdn.com/zustand@^5.0.9",
|
| 26 |
+
"zustand/": "https://aistudiocdn.com/zustand@^5.0.9/",
|
| 27 |
+
"vite": "https://aistudiocdn.com/vite@^7.2.6",
|
| 28 |
+
"@vitejs/plugin-react": "https://aistudiocdn.com/@vitejs/plugin-react@^5.1.1"
|
| 29 |
}
|
| 30 |
}
|
| 31 |
</script>
|
store.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
| 1 |
|
| 2 |
-
|
| 3 |
import { create } from 'zustand';
|
| 4 |
import { persist, createJSONStorage } from 'zustand/middleware';
|
| 5 |
-
import { UserAccount, LeadData, AdminConfig, WeddingStyle, GeneratedResult, CompositionMode, Resolution, SubjectType, GenerationStatus, Language, PointHistory, FeedbackItem } from './types';
|
| 6 |
import { TRANSLATIONS } from './constants/translations';
|
| 7 |
|
| 8 |
// --- Default Configuration ---
|
|
@@ -36,6 +35,7 @@ interface UserState {
|
|
| 36 |
leads: LeadData[];
|
| 37 |
feedback: FeedbackItem[];
|
| 38 |
guestFavorites: string[]; // NEW: Favorites for non-logged in users
|
|
|
|
| 39 |
|
| 40 |
// Actions
|
| 41 |
login: (phone: string, password?: string) => { success: boolean; msg: string };
|
|
@@ -44,7 +44,7 @@ interface UserState {
|
|
| 44 |
updateUser: (id: string, updates: Partial<UserAccount>) => void;
|
| 45 |
resetPassword: (phone: string, newPass: string) => boolean;
|
| 46 |
addPoints: (amount: number, reason: string) => void;
|
| 47 |
-
toggleFavorite: (styleId: string) => void;
|
| 48 |
|
| 49 |
// Admin Actions
|
| 50 |
addLead: (lead: LeadData) => void;
|
|
@@ -54,6 +54,9 @@ interface UserState {
|
|
| 54 |
|
| 55 |
// Feedback Actions
|
| 56 |
addFeedback: (item: FeedbackItem) => void;
|
|
|
|
|
|
|
|
|
|
| 57 |
}
|
| 58 |
|
| 59 |
export const useUserStore = create<UserState>()(
|
|
@@ -64,6 +67,7 @@ export const useUserStore = create<UserState>()(
|
|
| 64 |
leads: [],
|
| 65 |
feedback: [],
|
| 66 |
guestFavorites: [],
|
|
|
|
| 67 |
|
| 68 |
login: (phone, password) => {
|
| 69 |
const { allUsers } = get();
|
|
@@ -171,7 +175,10 @@ export const useUserStore = create<UserState>()(
|
|
| 171 |
setLeads: (leads) => set({ leads }),
|
| 172 |
|
| 173 |
// Feedback Actions
|
| 174 |
-
addFeedback: (item) => set(state => ({ feedback: [item, ...state.feedback] }))
|
|
|
|
|
|
|
|
|
|
| 175 |
}),
|
| 176 |
{
|
| 177 |
name: 'rl_user_storage',
|
|
@@ -180,7 +187,8 @@ export const useUserStore = create<UserState>()(
|
|
| 180 |
leads: state.leads,
|
| 181 |
currentUser: state.currentUser,
|
| 182 |
feedback: state.feedback,
|
| 183 |
-
guestFavorites: state.guestFavorites
|
|
|
|
| 184 |
}),
|
| 185 |
}
|
| 186 |
)
|
|
@@ -289,7 +297,7 @@ interface GenerationState {
|
|
| 289 |
|
| 290 |
setStatus: (status: GenerationStatus) => void;
|
| 291 |
setProgress: (progress: GenerationState['progress']) => void;
|
| 292 |
-
setErrorMsg: (msg
|
| 293 |
addResult: (result: GeneratedResult) => void;
|
| 294 |
setResults: (results: Record<string, GeneratedResult>) => void;
|
| 295 |
|
|
@@ -355,4 +363,4 @@ export const useGenerationStore = create<GenerationState>((set) => ({
|
|
| 355 |
setScanStep: (step) => set({ scanStep: step }),
|
| 356 |
setAnalysisData: (recs, result) => set({ recommendedStyleIds: recs, analysisResult: result }),
|
| 357 |
stopScanning: () => set({ isScanning: false })
|
| 358 |
-
}));
|
|
|
|
| 1 |
|
|
|
|
| 2 |
import { create } from 'zustand';
|
| 3 |
import { persist, createJSONStorage } from 'zustand/middleware';
|
| 4 |
+
import { UserAccount, LeadData, AdminConfig, WeddingStyle, GeneratedResult, CompositionMode, Resolution, SubjectType, GenerationStatus, Language, PointHistory, FeedbackItem, GenerationLog } from './types';
|
| 5 |
import { TRANSLATIONS } from './constants/translations';
|
| 6 |
|
| 7 |
// --- Default Configuration ---
|
|
|
|
| 35 |
leads: LeadData[];
|
| 36 |
feedback: FeedbackItem[];
|
| 37 |
guestFavorites: string[]; // NEW: Favorites for non-logged in users
|
| 38 |
+
logs: GenerationLog[]; // NEW: Analytics Logs
|
| 39 |
|
| 40 |
// Actions
|
| 41 |
login: (phone: string, password?: string) => { success: boolean; msg: string };
|
|
|
|
| 44 |
updateUser: (id: string, updates: Partial<UserAccount>) => void;
|
| 45 |
resetPassword: (phone: string, newPass: string) => boolean;
|
| 46 |
addPoints: (amount: number, reason: string) => void;
|
| 47 |
+
toggleFavorite: (styleId: string) => void;
|
| 48 |
|
| 49 |
// Admin Actions
|
| 50 |
addLead: (lead: LeadData) => void;
|
|
|
|
| 54 |
|
| 55 |
// Feedback Actions
|
| 56 |
addFeedback: (item: FeedbackItem) => void;
|
| 57 |
+
|
| 58 |
+
// Analytics Actions
|
| 59 |
+
addLog: (log: GenerationLog) => void;
|
| 60 |
}
|
| 61 |
|
| 62 |
export const useUserStore = create<UserState>()(
|
|
|
|
| 67 |
leads: [],
|
| 68 |
feedback: [],
|
| 69 |
guestFavorites: [],
|
| 70 |
+
logs: [],
|
| 71 |
|
| 72 |
login: (phone, password) => {
|
| 73 |
const { allUsers } = get();
|
|
|
|
| 175 |
setLeads: (leads) => set({ leads }),
|
| 176 |
|
| 177 |
// Feedback Actions
|
| 178 |
+
addFeedback: (item) => set(state => ({ feedback: [item, ...state.feedback] })),
|
| 179 |
+
|
| 180 |
+
// Analytics
|
| 181 |
+
addLog: (log) => set(state => ({ logs: [log, ...state.logs].slice(0, 2000) })), // Keep last 2000 logs
|
| 182 |
}),
|
| 183 |
{
|
| 184 |
name: 'rl_user_storage',
|
|
|
|
| 187 |
leads: state.leads,
|
| 188 |
currentUser: state.currentUser,
|
| 189 |
feedback: state.feedback,
|
| 190 |
+
guestFavorites: state.guestFavorites,
|
| 191 |
+
logs: state.logs
|
| 192 |
}),
|
| 193 |
}
|
| 194 |
)
|
|
|
|
| 297 |
|
| 298 |
setStatus: (status: GenerationStatus) => void;
|
| 299 |
setProgress: (progress: GenerationState['progress']) => void;
|
| 300 |
+
setErrorMsg: (msg) => set({ errorMsg: msg }),
|
| 301 |
addResult: (result: GeneratedResult) => void;
|
| 302 |
setResults: (results: Record<string, GeneratedResult>) => void;
|
| 303 |
|
|
|
|
| 363 |
setScanStep: (step) => set({ scanStep: step }),
|
| 364 |
setAnalysisData: (recs, result) => set({ recommendedStyleIds: recs, analysisResult: result }),
|
| 365 |
stopScanning: () => set({ isScanning: false })
|
| 366 |
+
}));
|
types.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
|
| 2 |
-
|
| 3 |
// Import React to support React.ReactNode type usage
|
| 4 |
import React from 'react';
|
| 5 |
|
|
@@ -64,6 +63,16 @@ export interface LeadData {
|
|
| 64 |
crmId?: string; // External ID
|
| 65 |
}
|
| 66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
export interface AdminConfig {
|
| 68 |
promoText: string;
|
| 69 |
promoEnds: string; // ISO Date string
|
|
@@ -148,4 +157,4 @@ export interface FeedbackItem {
|
|
| 148 |
content: string;
|
| 149 |
timestamp: number;
|
| 150 |
contact?: string;
|
| 151 |
-
}
|
|
|
|
| 1 |
|
|
|
|
| 2 |
// Import React to support React.ReactNode type usage
|
| 3 |
import React from 'react';
|
| 4 |
|
|
|
|
| 63 |
crmId?: string; // External ID
|
| 64 |
}
|
| 65 |
|
| 66 |
+
export interface GenerationLog {
|
| 67 |
+
styleId: string;
|
| 68 |
+
styleName: string;
|
| 69 |
+
userId: string;
|
| 70 |
+
timestamp: number;
|
| 71 |
+
ip: string;
|
| 72 |
+
location: string;
|
| 73 |
+
device: string;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
export interface AdminConfig {
|
| 77 |
promoText: string;
|
| 78 |
promoEnds: string; // ISO Date string
|
|
|
|
| 157 |
content: string;
|
| 158 |
timestamp: number;
|
| 159 |
contact?: string;
|
| 160 |
+
}
|