@elsapiens/cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,92 @@
1
1
  import React from 'react';
2
2
  import ReactDOM from 'react-dom/client';
3
3
  import { BrowserRouter } from 'react-router-dom';
4
+ import { HelpProvider, ThemeProvider, LocaleProvider, AuthProvider, useAuth } from '@elsapiens/providers';
5
+ import { AuthGuard } from '@elsapiens/shell';
4
6
  import App from './App';
5
- import '@elsapiens/styles/variables.css';
6
- import '@elsapiens/styles/components.css';
7
+ import { helpTopics } from './help-topics';
8
+ import { loadCommon } from './translations/common/index';
9
+ import { initializeServices } from './services';
7
10
  import './index.css';
8
11
 
12
+ // Configuration from environment
13
+ const AUTH_URL = import.meta.env.VITE_AUTH_URL || 'https://auth.example.com';
14
+ const ROOT_DOMAIN = import.meta.env.VITE_ROOT_DOMAIN ? `.${import.meta.env.VITE_ROOT_DOMAIN}` : '.example.com';
15
+
16
+ // Initialize services
17
+ initializeServices();
18
+
19
+ /**
20
+ * Loading screen component
21
+ */
22
+ function LoadingScreen() {
23
+ return (
24
+ <div className="min-h-screen flex items-center justify-center bg-background">
25
+ <div className="text-center el-space-y-md">
26
+ <div className="w-12 h-12 mx-auto rounded-lg bg-primary-500 flex items-center justify-center text-white font-bold text-xl animate-pulse">
27
+ E
28
+ </div>
29
+ <p className="text-muted-foreground">Loading...</p>
30
+ </div>
31
+ </div>
32
+ );
33
+ }
34
+
35
+ /**
36
+ * Inner app wrapper that uses the auth context for AuthGuard
37
+ *
38
+ * In development mode, auth is bypassed for easier testing.
39
+ * In production, set VITE_REQUIRE_AUTH=true to enable auth checks.
40
+ */
41
+ function AuthenticatedApp() {
42
+ const { isAuthenticated, isLoading, hasAllPermissions, hasAnyRole } = useAuth();
43
+
44
+ // Wait for auth to be determined before rendering AuthGuard
45
+ if (isLoading) {
46
+ return <LoadingScreen />;
47
+ }
48
+
49
+ // In development, bypass auth by default unless VITE_REQUIRE_AUTH is set
50
+ const requireAuth = import.meta.env.VITE_REQUIRE_AUTH === 'true';
51
+ const isAuthed = requireAuth ? isAuthenticated : true;
52
+
53
+ return (
54
+ <AuthGuard
55
+ authUrl={AUTH_URL}
56
+ returnUrlParam="return_url"
57
+ checkAuth={() => isAuthed}
58
+ checkPermissions={(perms) => hasAllPermissions(perms)}
59
+ checkRoles={(roles) => hasAnyRole(roles)}
60
+ loadingComponent={<LoadingScreen />}
61
+ >
62
+ <App />
63
+ </AuthGuard>
64
+ );
65
+ }
66
+
9
67
  ReactDOM.createRoot(document.getElementById('root')!).render(
10
68
  <React.StrictMode>
11
- <BrowserRouter>
12
- <App />
69
+ <BrowserRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
70
+ <ThemeProvider defaultTheme="system" rootDomain={ROOT_DOMAIN}>
71
+ <AuthProvider
72
+ authUrl={AUTH_URL}
73
+ returnUrlParam="return_url"
74
+ >
75
+ <LocaleProvider initialLoader={loadCommon} rootDomain={ROOT_DOMAIN}>
76
+ <HelpProvider
77
+ initialTopics={helpTopics}
78
+ config={{
79
+ enabled: true,
80
+ showHelpIcons: true,
81
+ helpPanelPosition: 'modal',
82
+ searchEnabled: true,
83
+ }}
84
+ >
85
+ <AuthenticatedApp />
86
+ </HelpProvider>
87
+ </LocaleProvider>
88
+ </AuthProvider>
89
+ </ThemeProvider>
13
90
  </BrowserRouter>
14
91
  </React.StrictMode>
15
92
  );
@@ -7,25 +7,50 @@
7
7
  "dev": "vite",
8
8
  "build": "tsc && vite build",
9
9
  "preview": "vite preview",
10
- "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
10
+ "lint": "eslint .",
11
+ "lint:fix": "eslint . --fix",
12
+ "test": "vitest",
13
+ "test:run": "vitest run",
14
+ "test:coverage": "vitest run --coverage",
15
+ "precommit": "pnpm test:coverage && pnpm lint",
16
+ "prepare": "husky"
11
17
  },
12
18
  "dependencies": {
13
19
  "@elsapiens/ui": "latest",
14
20
  "@elsapiens/utils": "latest",
15
21
  "@elsapiens/styles": "latest",
16
- "lucide-react": "^0.300.0",
22
+ "@elsapiens/providers": "latest",
23
+ "@elsapiens/layout": "latest",
24
+ "@elsapiens/shell": "latest",
25
+ "@elsapiens/help": "latest",
26
+ "@elsapiens/hooks": "latest",
27
+ "@elsapiens/services": "latest",
28
+ "@elsapiens/sdk": "latest",
29
+ "lucide-react": "^0.303.0",
17
30
  "react": "^18.2.0",
18
31
  "react-dom": "^18.2.0",
19
32
  "react-router-dom": "^6.21.0"
20
33
  },
21
34
  "devDependencies": {
35
+ "@eslint/js": "^9.0.0",
36
+ "@testing-library/react": "^14.0.0",
37
+ "@testing-library/jest-dom": "^6.0.0",
22
38
  "@types/react": "^18.2.0",
23
39
  "@types/react-dom": "^18.2.0",
24
40
  "@vitejs/plugin-react": "^4.2.0",
41
+ "@vitest/coverage-v8": "^1.0.0",
25
42
  "autoprefixer": "^10.4.16",
43
+ "eslint": "^9.0.0",
44
+ "eslint-plugin-react-hooks": "^5.0.0",
45
+ "eslint-plugin-react-refresh": "^0.4.0",
46
+ "globals": "^15.0.0",
47
+ "husky": "^9.0.0",
48
+ "jsdom": "^24.0.0",
26
49
  "postcss": "^8.4.32",
27
50
  "tailwindcss": "^3.4.0",
28
51
  "typescript": "^5.3.0",
29
- "vite": "^5.0.0"
52
+ "typescript-eslint": "^8.0.0",
53
+ "vite": "^5.0.0",
54
+ "vitest": "^1.0.0"
30
55
  }
31
56
  }
@@ -1,11 +1,18 @@
1
+ import { useState } from 'react';
1
2
  import {
2
3
  Button,
3
4
  Card,
4
5
  CardHeader,
5
6
  CardTitle,
6
7
  CardContent,
8
+ Modal,
9
+ Input,
10
+ FormField,
7
11
  } from '@elsapiens/ui';
12
+ import { PageHeaderWithBreadcrumbs } from '@elsapiens/layout';
8
13
  import { Plus, TrendingUp, TrendingDown, Users, FileText, DollarSign } from 'lucide-react';
14
+ import { Link } from 'react-router-dom';
15
+ import { useLocale, usePageHeader } from '@elsapiens/providers';
9
16
 
10
17
  interface StatCardProps {
11
18
  title: string;
@@ -19,13 +26,13 @@ function StatCard({ title, value, change, changeType, icon: Icon }: StatCardProp
19
26
  const isPositive = changeType === 'positive';
20
27
  return (
21
28
  <Card padding="none">
22
- <CardContent className="p-6">
29
+ <CardContent className="el-p-lg">
23
30
  <div className="flex items-start justify-between">
24
31
  <div className="flex-1">
25
32
  <p className="text-sm text-muted-foreground">{title}</p>
26
- <p className="text-2xl font-bold text-foreground mt-1">{value}</p>
33
+ <p className="text-2xl font-bold text-foreground el-mt-xs">{value}</p>
27
34
  {change && (
28
- <p className={`text-sm mt-1 flex items-center gap-1 ${isPositive ? 'text-success' : 'text-error'}`}>
35
+ <p className={`text-sm el-mt-xs flex items-center el-gap-xs ${isPositive ? 'text-success' : 'text-error'}`}>
29
36
  {isPositive ? <TrendingUp className="w-4 h-4" /> : <TrendingDown className="w-4 h-4" />}
30
37
  {change}
31
38
  </p>
@@ -41,68 +48,100 @@ function StatCard({ title, value, change, changeType, icon: Icon }: StatCardProp
41
48
  }
42
49
 
43
50
  export default function Dashboard() {
51
+ const [isModalOpen, setIsModalOpen] = useState(false);
52
+ const { t } = useLocale();
53
+
54
+ // Set page header info
55
+ usePageHeader({
56
+ title: 'Dashboard',
57
+ description: "Welcome back! Here's what's happening.",
58
+ breadcrumbs: [
59
+ { label: t('nav.home'), href: '/', as: Link },
60
+ { label: 'Dashboard' },
61
+ ],
62
+ });
63
+
44
64
  return (
45
- <div className="p-6">
46
- {/* Page Header - pl-6 to align with content */}
47
- <div className="flex items-center justify-between mb-8 pl-6">
48
- <div>
49
- <h1 className="text-2xl font-bold text-foreground">Dashboard</h1>
50
- <p className="text-muted-foreground">Welcome back! Here's what's happening.</p>
51
- </div>
52
- <div className="flex items-center gap-4">
53
- <Button>
54
- <Plus className="w-4 h-4 mr-2" />
55
- New Item
56
- </Button>
65
+ <div className="flex flex-col">
66
+ <PageHeaderWithBreadcrumbs variant="card" linkComponent={Link}>
67
+ <Button onClick={() => setIsModalOpen(true)} className="w-full sm:w-auto">
68
+ <Plus className="w-4 h-4 el-mr-sm" />
69
+ New Item
70
+ </Button>
71
+ </PageHeaderWithBreadcrumbs>
72
+
73
+ <div className="el-p-lg" style={{ overflowX: 'clip' }}>
74
+ {/* Stats Grid */}
75
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 el-gap-lg el-mb-xl">
76
+ <StatCard
77
+ title="Total Revenue"
78
+ value="$45,231"
79
+ change="+20.1% from last month"
80
+ changeType="positive"
81
+ icon={DollarSign}
82
+ />
83
+ <StatCard
84
+ title="Documents"
85
+ value="2,345"
86
+ change="+15% from last month"
87
+ changeType="positive"
88
+ icon={FileText}
89
+ />
90
+ <StatCard
91
+ title="Active Users"
92
+ value="1,234"
93
+ change="+5.2% from last month"
94
+ changeType="positive"
95
+ icon={Users}
96
+ />
97
+ <StatCard
98
+ title="Bounce Rate"
99
+ value="24.5%"
100
+ change="-3.2% from last month"
101
+ changeType="negative"
102
+ icon={TrendingDown}
103
+ />
57
104
  </div>
58
- </div>
59
105
 
60
- {/* Stats Grid */}
61
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
62
- <StatCard
63
- title="Total Revenue"
64
- value="$45,231"
65
- change="+20.1% from last month"
66
- changeType="positive"
67
- icon={DollarSign}
68
- />
69
- <StatCard
70
- title="Documents"
71
- value="2,345"
72
- change="+15% from last month"
73
- changeType="positive"
74
- icon={FileText}
75
- />
76
- <StatCard
77
- title="Active Users"
78
- value="1,234"
79
- change="+5.2% from last month"
80
- changeType="positive"
81
- icon={Users}
82
- />
83
- <StatCard
84
- title="Bounce Rate"
85
- value="24.5%"
86
- change="-3.2% from last month"
87
- changeType="negative"
88
- icon={TrendingDown}
89
- />
106
+ {/* Content Area */}
107
+ <div>
108
+ <Card padding="none">
109
+ <CardHeader className="el-p-lg">
110
+ <CardTitle>Getting Started</CardTitle>
111
+ </CardHeader>
112
+ <CardContent className="el-px-lg el-pb-lg">
113
+ <p className="text-muted-foreground">
114
+ Welcome to your new elSapiens project! Edit this page at{' '}
115
+ <code className="text-sm bg-muted el-px-xs el-py-xs rounded">src/pages/Dashboard.tsx</code>
116
+ </p>
117
+ </CardContent>
118
+ </Card>
119
+ </div>
90
120
  </div>
91
121
 
92
- {/* Content Area */}
93
- <div>
94
- <Card padding="none">
95
- <CardHeader className="p-6">
96
- <CardTitle>Getting Started</CardTitle>
97
- </CardHeader>
98
- <CardContent className="p-6 pt-0">
99
- <p className="text-muted-foreground">
100
- Welcome to your new elSapiens project! Edit this page at{' '}
101
- <code className="text-sm bg-muted px-1 py-0.5 rounded">src/pages/Dashboard.tsx</code>
102
- </p>
103
- </CardContent>
104
- </Card>
105
- </div>
122
+ {/* Modal Example */}
123
+ <Modal
124
+ isOpen={isModalOpen}
125
+ onClose={() => setIsModalOpen(false)}
126
+ title="Create New Item"
127
+ >
128
+ <div className="flex flex-col el-gap-field">
129
+ <FormField label="Item Name">
130
+ <Input placeholder="Enter item name..." />
131
+ </FormField>
132
+ <FormField label="Description">
133
+ <Input placeholder="Enter description..." />
134
+ </FormField>
135
+ <div className="flex justify-end el-gap-field el-pt-sm">
136
+ <Button variant="outline" onClick={() => setIsModalOpen(false)}>
137
+ Cancel
138
+ </Button>
139
+ <Button onClick={() => setIsModalOpen(false)}>
140
+ Create
141
+ </Button>
142
+ </div>
143
+ </div>
144
+ </Modal>
106
145
  </div>
107
146
  );
108
147
  }