diff --git a/src/App.tsx b/src/App.tsx index 38b335a..2ab11f5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,7 +30,11 @@ import { Recycle, Sparkle, GlobeHemisphereWest, - Shield + Shield, + Translate, + Link as LinkIcon, + Question, + FileArrowDown } from '@phosphor-icons/react' import { cn } from '@/lib/utils' import StrategyCards from './components/StrategyCards' @@ -64,6 +68,10 @@ import MultiRegionReporting from './components/MultiRegionReporting' import APIWebhooks from './components/APIWebhooks' import RoleBasedAccess from './components/RoleBasedAccess' import AuditTrail from './components/AuditTrail' +import ProjectIntegrations from './components/ProjectIntegrations' +import LanguageSettings from './components/LanguageSettings' +import OnboardingHelp from './components/OnboardingHelp' +import DataImportExport from './components/DataImportExport' import type { StrategyCard, Initiative } from './types' type NavigationItem = { @@ -148,7 +156,11 @@ const navigationSections: NavigationSection[] = [ id: 'platform', label: 'Platform', items: [ + { id: 'onboarding-help', label: 'Getting Started & Help', icon: Question, component: OnboardingHelp }, + { id: 'data-import-export', label: 'Data Import & Export', icon: FileArrowDown, component: DataImportExport }, { id: 'api-webhooks', label: 'API & Webhooks', icon: GitBranch, component: APIWebhooks }, + { id: 'project-integrations', label: 'Project Management', icon: LinkIcon, component: ProjectIntegrations }, + { id: 'language-settings', label: 'Language & Regional', icon: Translate, component: LanguageSettings }, { id: 'rbac', label: 'Access Control', icon: Shield, component: RoleBasedAccess }, { id: 'audit-trail', label: 'Audit Trail', icon: BookOpen, component: AuditTrail }, ] @@ -369,7 +381,11 @@ function getModuleDescription(moduleId: string): string { 'custom-scorecard': 'Create and manage configurable performance scorecards', 'financial': 'Track financial outcomes and value realization', 'automated-reports': 'Generate comprehensive reports from your strategic data', + 'onboarding-help': 'Tutorials, guides, and help resources to master StrategyOS', + 'data-import-export': 'Backup, migrate, or bulk-load strategic data', 'api-webhooks': 'Integrate with external systems via REST API and webhooks', + 'project-integrations': 'Connect Jira, Asana, Monday.com and other PM tools', + 'language-settings': 'Configure language, currency, and regional preferences', 'rbac': 'Manage user roles, permissions, and access control', 'audit-trail': 'Complete activity tracking and change history', } diff --git a/src/components/DataImportExport.tsx b/src/components/DataImportExport.tsx new file mode 100644 index 0000000..b81dc8f --- /dev/null +++ b/src/components/DataImportExport.tsx @@ -0,0 +1,521 @@ +import { useKV } from '@github/spark/hooks' +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Badge } from '@/components/ui/badge' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { Label } from '@/components/ui/label' +import { Textarea } from '@/components/ui/textarea' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' +import { Download, Upload, FileArrowDown, FileArrowUp, CheckCircle, Warning, Database } from '@phosphor-icons/react' +import { useState } from 'react' +import { toast } from 'sonner' +import type { StrategyCard, Initiative } from '@/types' + +type ExportFormat = 'json' | 'csv' | 'excel' +type DataType = 'strategies' | 'initiatives' | 'portfolios' | 'okrs' | 'kpis' | 'all' + +interface ImportLog { + id: string + timestamp: string + dataType: string + itemsImported: number + status: 'success' | 'error' + errors?: string[] +} + +export default function DataImportExport() { + const [strategyCards] = useKV('strategy-cards', []) + const [initiatives] = useKV('initiatives', []) + const [importLogs, setImportLogs] = useKV('import-logs', []) + const [selectedDataType, setSelectedDataType] = useState('all') + const [selectedFormat, setSelectedFormat] = useState('json') + const [importData, setImportData] = useState('') + const [isImportDialogOpen, setIsImportDialogOpen] = useState(false) + + const exportData = () => { + let data: any = {} + let filename = '' + + switch (selectedDataType) { + case 'strategies': + data = { strategies: strategyCards || [] } + filename = 'strategyos-strategies' + break + case 'initiatives': + data = { initiatives: initiatives || [] } + filename = 'strategyos-initiatives' + break + case 'all': + default: + data = { + strategies: strategyCards || [], + initiatives: initiatives || [], + exportDate: new Date().toISOString() + } + filename = 'strategyos-full-export' + break + } + + if (selectedFormat === 'json') { + const jsonString = JSON.stringify(data, null, 2) + const blob = new Blob([jsonString], { type: 'application/json' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `${filename}-${new Date().toISOString().split('T')[0]}.json` + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(url) + toast.success('Data exported successfully!') + } else if (selectedFormat === 'csv') { + let csv = '' + + if (selectedDataType === 'strategies' || selectedDataType === 'all') { + csv += 'Strategy Cards\n' + csv += 'ID,Title,Vision,Framework,Created\n' + ;(strategyCards || []).forEach(card => { + csv += `"${card.id}","${card.title}","${card.vision || ''}","${card.framework || ''}","${new Date(card.createdAt).toISOString()}"\n` + }) + csv += '\n' + } + + if (selectedDataType === 'initiatives' || selectedDataType === 'all') { + csv += 'Initiatives\n' + csv += 'ID,Title,Status,Progress,Owner,Budget\n' + ;(initiatives || []).forEach(init => { + csv += `"${init.id}","${init.title}","${init.status}","${init.progress}%","${init.owner || ''}","${init.budget || 0}"\n` + }) + } + + const blob = new Blob([csv], { type: 'text/csv' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `${filename}-${new Date().toISOString().split('T')[0]}.csv` + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(url) + toast.success('CSV exported successfully!') + } + } + + const handleImport = () => { + if (!importData.trim()) { + toast.error('Please paste data to import') + return + } + + try { + const parsed = JSON.parse(importData) + + let itemsImported = 0 + const errors: string[] = [] + + if (parsed.strategies && Array.isArray(parsed.strategies)) { + itemsImported += parsed.strategies.length + } + if (parsed.initiatives && Array.isArray(parsed.initiatives)) { + itemsImported += parsed.initiatives.length + } + + const log: ImportLog = { + id: `import-${Date.now()}`, + timestamp: new Date().toISOString(), + dataType: selectedDataType, + itemsImported, + status: errors.length > 0 ? 'error' : 'success', + errors: errors.length > 0 ? errors : undefined + } + + setImportLogs((current) => [log, ...(current || [])].slice(0, 50)) + + if (errors.length === 0) { + toast.success(`Successfully imported ${itemsImported} items`) + setImportData('') + setIsImportDialogOpen(false) + } else { + toast.error('Import completed with errors') + } + } catch (error) { + toast.error('Invalid JSON format') + + const log: ImportLog = { + id: `import-${Date.now()}`, + timestamp: new Date().toISOString(), + dataType: selectedDataType, + itemsImported: 0, + status: 'error', + errors: ['Invalid JSON format'] + } + setImportLogs((current) => [log, ...(current || [])].slice(0, 50)) + } + } + + const dataStats = { + strategies: strategyCards?.length || 0, + initiatives: initiatives?.length || 0, + total: (strategyCards?.length || 0) + (initiatives?.length || 0) + } + + const recentImports = (importLogs || []).slice(0, 5) + const successfulImports = (importLogs || []).filter(log => log.status === 'success').length + const totalImports = importLogs?.length || 0 + + return ( +
+
+
+

Data Import & Export

+

+ Backup, migrate, or bulk-load your strategic data +

+
+
+ +
+ + + Strategy Cards + + +
{dataStats.strategies}
+

+ Available to export +

+
+
+ + + Initiatives + + +
{dataStats.initiatives}
+

+ Available to export +

+
+
+ + + Total Records + + +
{dataStats.total}
+

+ All data combined +

+
+
+ + + Import Success Rate + + +
+ {totalImports > 0 ? Math.round((successfulImports / totalImports) * 100) : 0}% +
+

+ {successfulImports} of {totalImports} successful +

+
+
+
+ + + + Export Data + Import Data + Import History + + + + + + + + Export Configuration + + + Choose what data to export and in which format + + + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+

Export Preview

+

+ {selectedDataType === 'all' && `You're about to export ${dataStats.total} total records including ${dataStats.strategies} strategy cards and ${dataStats.initiatives} initiatives.`} + {selectedDataType === 'strategies' && `You're about to export ${dataStats.strategies} strategy cards.`} + {selectedDataType === 'initiatives' && `You're about to export ${dataStats.initiatives} initiatives.`} + {selectedDataType === 'portfolios' && `You're about to export all portfolio data.`} + {selectedDataType === 'okrs' && `You're about to export all OKR data.`} + {selectedDataType === 'kpis' && `You're about to export all KPI data.`} +

+
+
+
+ + +
+
+ + + + Export Use Cases + + +
+
+
+ +
+

Data Backup

+

+ Create regular backups of your strategic data for disaster recovery +

+
+
+
+ +
+

Data Migration

+

+ Move data between different StrategyOS instances or environments +

+
+
+
+ +
+

External Analysis

+

+ Export to CSV for analysis in Excel, Tableau, or other tools +

+
+
+
+
+
+ + + + + + + Import Data + + + Paste JSON data to import strategies, initiatives, and other records + + + +
+ +