@lucifer91299/create-portal-app 1.0.9 → 1.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.
- package/dist/cli/index.js +499 -4
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -37,7 +37,7 @@ function closePrompt() {
|
|
|
37
37
|
// src/templates/index.ts
|
|
38
38
|
function genPackageJson(o) {
|
|
39
39
|
const deps = {
|
|
40
|
-
"@lucifer91299/ui": o.localUiPath ? `file:${o.localUiPath}` : "^1.
|
|
40
|
+
"@lucifer91299/ui": o.localUiPath ? `file:${o.localUiPath}` : "^1.1.2",
|
|
41
41
|
"next": "^15.3.0",
|
|
42
42
|
"react": "^19.0.0",
|
|
43
43
|
"react-dom": "^19.0.0",
|
|
@@ -400,7 +400,7 @@ export default api
|
|
|
400
400
|
`;
|
|
401
401
|
}
|
|
402
402
|
function genNavConfig(o) {
|
|
403
|
-
return `import { LayoutDashboard, Settings, Users,
|
|
403
|
+
return `import { LayoutDashboard, Settings, Users, Layers } from 'lucide-react'
|
|
404
404
|
import type { NavGroup } from '@lucifer91299/ui'
|
|
405
405
|
|
|
406
406
|
export const navGroups: NavGroup[] = [
|
|
@@ -408,8 +408,9 @@ export const navGroups: NavGroup[] = [
|
|
|
408
408
|
heading: 'Main',
|
|
409
409
|
groupIcon: <LayoutDashboard className="h-3.5 w-3.5" />,
|
|
410
410
|
items: [
|
|
411
|
-
{ label: 'Dashboard',
|
|
412
|
-
{ label: 'Users',
|
|
411
|
+
{ label: 'Dashboard', href: '/dashboard', icon: <LayoutDashboard className="h-4 w-4" /> },
|
|
412
|
+
{ label: 'Users', href: '/dashboard/users', icon: <Users className="h-4 w-4" /> },
|
|
413
|
+
{ label: 'Components', href: '/dashboard/components', icon: <Layers className="h-4 w-4" /> },
|
|
413
414
|
],
|
|
414
415
|
},
|
|
415
416
|
{
|
|
@@ -833,6 +834,499 @@ export default function SettingsPage() {
|
|
|
833
834
|
}
|
|
834
835
|
`;
|
|
835
836
|
}
|
|
837
|
+
function genComponentsShowcasePage() {
|
|
838
|
+
return `'use client'
|
|
839
|
+
|
|
840
|
+
import { useState } from 'react'
|
|
841
|
+
import {
|
|
842
|
+
PageShell, Breadcrumbs, Card, TricolorBar,
|
|
843
|
+
Badge, StatusBadge, AlertBanner, LoadingSpinner,
|
|
844
|
+
Button, Input, Select, Textarea, Switch, Checkbox, RadioGroup, DatePicker,
|
|
845
|
+
Dialog, Tooltip, Tabs, TabsList, TabsTrigger, TabsContent,
|
|
846
|
+
Accordion, AccordionItem, Progress, Skeleton, SkeletonCard, SkeletonText,
|
|
847
|
+
Separator, Avatar, AvatarGroup, DataTable, ActionButtons,
|
|
848
|
+
PortalBarChart, PortalLineChart, PortalAreaChart, PortalDonutChart,
|
|
849
|
+
} from '@lucifer91299/ui'
|
|
850
|
+
|
|
851
|
+
// \u2500\u2500 Demo data \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
852
|
+
|
|
853
|
+
const BAR_DATA = [
|
|
854
|
+
{ month: 'Jan', revenue: 42, expenses: 28 },
|
|
855
|
+
{ month: 'Feb', revenue: 55, expenses: 31 },
|
|
856
|
+
{ month: 'Mar', revenue: 48, expenses: 29 },
|
|
857
|
+
{ month: 'Apr', revenue: 63, expenses: 35 },
|
|
858
|
+
{ month: 'May', revenue: 71, expenses: 38 },
|
|
859
|
+
{ month: 'Jun', revenue: 58, expenses: 32 },
|
|
860
|
+
]
|
|
861
|
+
|
|
862
|
+
const DONUT_DATA = [
|
|
863
|
+
{ label: 'Active', value: 58 },
|
|
864
|
+
{ label: 'Pending', value: 22 },
|
|
865
|
+
{ label: 'Inactive', value: 12 },
|
|
866
|
+
{ label: 'Blocked', value: 8 },
|
|
867
|
+
]
|
|
868
|
+
|
|
869
|
+
const TABLE_DATA = [
|
|
870
|
+
{ id: 1, name: 'Priya Mehta', role: 'Admin', status: 'active', joined: '12 Jan 2024' },
|
|
871
|
+
{ id: 2, name: 'Arjun Sharma', role: 'Manager', status: 'pending', joined: '03 Feb 2024' },
|
|
872
|
+
{ id: 3, name: 'Neha Gupta', role: 'Viewer', status: 'inactive', joined: '22 Mar 2024' },
|
|
873
|
+
{ id: 4, name: 'Ravi Patel', role: 'Editor', status: 'active', joined: '05 Apr 2024' },
|
|
874
|
+
]
|
|
875
|
+
|
|
876
|
+
const SELECT_OPTS = [
|
|
877
|
+
{ value: 'admin', label: 'Administrator' },
|
|
878
|
+
{ value: 'manager', label: 'Manager' },
|
|
879
|
+
{ value: 'viewer', label: 'Viewer' },
|
|
880
|
+
]
|
|
881
|
+
|
|
882
|
+
const RADIO_OPTS = [
|
|
883
|
+
{ value: 'monthly', label: 'Monthly', description: 'Billed every month' },
|
|
884
|
+
{ value: 'quarterly', label: 'Quarterly', description: 'Save 10% quarterly' },
|
|
885
|
+
{ value: 'annual', label: 'Annual', description: 'Save 25% annually' },
|
|
886
|
+
]
|
|
887
|
+
|
|
888
|
+
const ACCORDION_ITEMS = [
|
|
889
|
+
{ value: 'q1', trigger: 'What components are included?',
|
|
890
|
+
body: 'Buttons, inputs, selects, datepickers, charts, tables, modals, tabs, accordions, and more. Everything you need for a production admin portal.' },
|
|
891
|
+
{ value: 'q2', trigger: 'How do I theme the components?',
|
|
892
|
+
body: 'Wrap your app in ThemeProvider with a createTheme() config. Every component reads CSS variables \u2014 no class overrides needed.' },
|
|
893
|
+
{ value: 'q3', trigger: 'Do I need recharts?',
|
|
894
|
+
body: 'Only for chart components (PortalBarChart, PortalLineChart, etc.). Install it separately: npm install recharts' },
|
|
895
|
+
]
|
|
896
|
+
|
|
897
|
+
// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
898
|
+
|
|
899
|
+
function Section({ id, title, subtitle, children }: {
|
|
900
|
+
id?: string; title: string; subtitle?: string; children: React.ReactNode
|
|
901
|
+
}) {
|
|
902
|
+
return (
|
|
903
|
+
<section id={id} className="space-y-4">
|
|
904
|
+
<div>
|
|
905
|
+
<h2 className="text-title3 font-bold text-label-primary">{title}</h2>
|
|
906
|
+
{subtitle && <p className="text-callout text-label-tertiary mt-1">{subtitle}</p>}
|
|
907
|
+
</div>
|
|
908
|
+
{children}
|
|
909
|
+
</section>
|
|
910
|
+
)
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function Row({ children }: { children: React.ReactNode }) {
|
|
914
|
+
return <div className="flex flex-wrap items-center gap-3">{children}</div>
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
function Label({ children }: { children: React.ReactNode }) {
|
|
918
|
+
return (
|
|
919
|
+
<p className="text-[11px] font-bold uppercase tracking-widest text-label-tertiary mb-3">
|
|
920
|
+
{children}
|
|
921
|
+
</p>
|
|
922
|
+
)
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// \u2500\u2500 Page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
926
|
+
|
|
927
|
+
export default function ComponentsPage() {
|
|
928
|
+
const [dialogOpen, setDialogOpen] = useState(false)
|
|
929
|
+
const [confirmOpen, setConfirmOpen] = useState(false)
|
|
930
|
+
const [tabVariant, setTabVariant] = useState<'line' | 'pill' | 'card'>('line')
|
|
931
|
+
const [selectVal, setSelectVal] = useState('admin')
|
|
932
|
+
const [radioVal, setRadioVal] = useState('monthly')
|
|
933
|
+
const [sw1, setSw1] = useState(true)
|
|
934
|
+
const [sw2, setSw2] = useState(false)
|
|
935
|
+
const [cb1, setCb1] = useState(true)
|
|
936
|
+
const [cb2, setCb2] = useState(false)
|
|
937
|
+
|
|
938
|
+
return (
|
|
939
|
+
<div className="p-6 space-y-14 max-w-5xl pb-20">
|
|
940
|
+
|
|
941
|
+
<PageShell
|
|
942
|
+
title="Component Gallery"
|
|
943
|
+
subtitle="Every component in the @lucifer91299/ui SDK \u2014 live and interactive."
|
|
944
|
+
breadcrumbs={
|
|
945
|
+
<Breadcrumbs items={[
|
|
946
|
+
{ label: 'Dashboard', href: '/dashboard' },
|
|
947
|
+
{ label: 'Components' },
|
|
948
|
+
]} />
|
|
949
|
+
}
|
|
950
|
+
/>
|
|
951
|
+
|
|
952
|
+
{/* \u2500\u2500 Buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
953
|
+
<Section id="buttons" title="Button" subtitle="5 semantic variants \xB7 3 sizes \xB7 loading and disabled states.">
|
|
954
|
+
<Card className="p-5 space-y-5">
|
|
955
|
+
<div>
|
|
956
|
+
<Label>Variants</Label>
|
|
957
|
+
<Row>
|
|
958
|
+
<Button variant="primary">Primary</Button>
|
|
959
|
+
<Button variant="accent">Accent</Button>
|
|
960
|
+
<Button variant="tinted">Tinted</Button>
|
|
961
|
+
<Button variant="outline">Outline</Button>
|
|
962
|
+
<Button variant="danger">Danger</Button>
|
|
963
|
+
</Row>
|
|
964
|
+
</div>
|
|
965
|
+
<Separator />
|
|
966
|
+
<div>
|
|
967
|
+
<Label>Sizes</Label>
|
|
968
|
+
<Row>
|
|
969
|
+
<Button variant="primary" size="sm">Small</Button>
|
|
970
|
+
<Button variant="primary" size="md">Medium</Button>
|
|
971
|
+
<Button variant="primary" size="lg">Large</Button>
|
|
972
|
+
</Row>
|
|
973
|
+
</div>
|
|
974
|
+
<Separator />
|
|
975
|
+
<div>
|
|
976
|
+
<Label>States</Label>
|
|
977
|
+
<Row>
|
|
978
|
+
<Button variant="primary" isLoading>Saving\u2026</Button>
|
|
979
|
+
<Button variant="primary" disabled>Disabled</Button>
|
|
980
|
+
</Row>
|
|
981
|
+
</div>
|
|
982
|
+
</Card>
|
|
983
|
+
</Section>
|
|
984
|
+
|
|
985
|
+
{/* \u2500\u2500 Badges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
986
|
+
<Section id="badges" title="Badge & StatusBadge" subtitle="Semantic chips for labels and workflow states.">
|
|
987
|
+
<Card className="p-5 space-y-5">
|
|
988
|
+
<div>
|
|
989
|
+
<Label>Badge variants</Label>
|
|
990
|
+
<Row>
|
|
991
|
+
<Badge variant="primary">Primary</Badge>
|
|
992
|
+
<Badge variant="active">Active</Badge>
|
|
993
|
+
<Badge variant="pending">Pending</Badge>
|
|
994
|
+
<Badge variant="inactive">Inactive</Badge>
|
|
995
|
+
<Badge variant="rejected">Rejected</Badge>
|
|
996
|
+
</Row>
|
|
997
|
+
</div>
|
|
998
|
+
<Separator />
|
|
999
|
+
<div>
|
|
1000
|
+
<Label>StatusBadge \u2014 all workflow states</Label>
|
|
1001
|
+
<Row>
|
|
1002
|
+
{['active','pending','approved','rejected','completed','paid','scheduled','inactive','cancelled'].map((s) => (
|
|
1003
|
+
<StatusBadge key={s} status={s} />
|
|
1004
|
+
))}
|
|
1005
|
+
</Row>
|
|
1006
|
+
</div>
|
|
1007
|
+
</Card>
|
|
1008
|
+
</Section>
|
|
1009
|
+
|
|
1010
|
+
{/* \u2500\u2500 AlertBanner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1011
|
+
<Section id="alerts" title="AlertBanner" subtitle="Contextual inline feedback messages.">
|
|
1012
|
+
<div className="space-y-3">
|
|
1013
|
+
<AlertBanner variant="info">Informational \u2014 your session expires in 30 minutes.</AlertBanner>
|
|
1014
|
+
<AlertBanner variant="success">Success \u2014 your changes have been saved successfully.</AlertBanner>
|
|
1015
|
+
<AlertBanner variant="warning">Warning \u2014 this action cannot be undone.</AlertBanner>
|
|
1016
|
+
<AlertBanner variant="error">Error \u2014 failed to connect to the server.</AlertBanner>
|
|
1017
|
+
</div>
|
|
1018
|
+
</Section>
|
|
1019
|
+
|
|
1020
|
+
{/* \u2500\u2500 Avatar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1021
|
+
<Section id="avatar" title="Avatar & AvatarGroup" subtitle="User profile images with initials fallback.">
|
|
1022
|
+
<Card className="p-5 space-y-5">
|
|
1023
|
+
<div>
|
|
1024
|
+
<Label>Sizes (xs \u2192 xl)</Label>
|
|
1025
|
+
<Row>
|
|
1026
|
+
<Avatar name="Priya Mehta" size="xs" />
|
|
1027
|
+
<Avatar name="Arjun Sharma" size="sm" />
|
|
1028
|
+
<Avatar name="Neha Gupta" size="md" />
|
|
1029
|
+
<Avatar name="Ravi Patel" size="lg" />
|
|
1030
|
+
<Avatar name="Sunita Rao" size="xl" />
|
|
1031
|
+
</Row>
|
|
1032
|
+
</div>
|
|
1033
|
+
<Separator />
|
|
1034
|
+
<div>
|
|
1035
|
+
<Label>AvatarGroup (max 4 visible)</Label>
|
|
1036
|
+
<AvatarGroup
|
|
1037
|
+
avatars={[
|
|
1038
|
+
{ name: 'Priya Mehta' },
|
|
1039
|
+
{ name: 'Arjun Sharma' },
|
|
1040
|
+
{ name: 'Neha Gupta' },
|
|
1041
|
+
{ name: 'Ravi Patel' },
|
|
1042
|
+
{ name: 'Sunita Rao' },
|
|
1043
|
+
{ name: 'Kiran Das' },
|
|
1044
|
+
]}
|
|
1045
|
+
max={4}
|
|
1046
|
+
/>
|
|
1047
|
+
</div>
|
|
1048
|
+
</Card>
|
|
1049
|
+
</Section>
|
|
1050
|
+
|
|
1051
|
+
{/* \u2500\u2500 Inputs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1052
|
+
<Section id="inputs" title="Input, Select & Textarea" subtitle="Form controls with labels, helpers, and validation states.">
|
|
1053
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
1054
|
+
<Input label="Full name" placeholder="Priya Mehta" />
|
|
1055
|
+
<Input label="Email" type="email" placeholder="priya@example.com" />
|
|
1056
|
+
<Input label="With error" error="This field is required" placeholder="..." />
|
|
1057
|
+
<Input label="Disabled" disabled defaultValue="Read-only value" />
|
|
1058
|
+
<Select label="Role" options={SELECT_OPTS} value={selectVal} onChange={setSelectVal} />
|
|
1059
|
+
<Input label="Password" type="password" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" />
|
|
1060
|
+
<div className="sm:col-span-2">
|
|
1061
|
+
<Textarea label="Message" placeholder="Type your message here\u2026" helperText="Max 500 characters." />
|
|
1062
|
+
</div>
|
|
1063
|
+
<div className="sm:col-span-2">
|
|
1064
|
+
<Textarea label="Textarea with error" error="Message cannot be empty." />
|
|
1065
|
+
</div>
|
|
1066
|
+
</div>
|
|
1067
|
+
</Section>
|
|
1068
|
+
|
|
1069
|
+
{/* \u2500\u2500 Switch, Checkbox, RadioGroup \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1070
|
+
<Section id="toggles" title="Switch, Checkbox & RadioGroup" subtitle="Selection controls for settings and forms.">
|
|
1071
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-5">
|
|
1072
|
+
<Card className="p-5 space-y-4">
|
|
1073
|
+
<Label>Switch</Label>
|
|
1074
|
+
<Switch label="Email notifications" description="Receive daily digest emails" checked={sw1} onChange={setSw1} />
|
|
1075
|
+
<Switch label="SMS alerts" description="Critical alerts only" checked={sw2} onChange={setSw2} />
|
|
1076
|
+
<Switch label="Disabled" disabled />
|
|
1077
|
+
</Card>
|
|
1078
|
+
<Card className="p-5 space-y-4">
|
|
1079
|
+
<Label>Checkbox</Label>
|
|
1080
|
+
<Checkbox label="Accept terms" description="I agree to the terms" checked={cb1} onChange={setCb1} />
|
|
1081
|
+
<Checkbox label="Subscribe" description="Newsletter opt-in" checked={cb2} onChange={setCb2} />
|
|
1082
|
+
<Checkbox label="Disabled" disabled />
|
|
1083
|
+
<Checkbox label="Indeterminate" indeterminate />
|
|
1084
|
+
</Card>
|
|
1085
|
+
<Card className="p-5">
|
|
1086
|
+
<Label>RadioGroup</Label>
|
|
1087
|
+
<RadioGroup options={RADIO_OPTS} value={radioVal} onChange={setRadioVal} />
|
|
1088
|
+
</Card>
|
|
1089
|
+
</div>
|
|
1090
|
+
</Section>
|
|
1091
|
+
|
|
1092
|
+
{/* \u2500\u2500 DatePicker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1093
|
+
<Section id="datepicker" title="DatePicker" subtitle="3-level calendar (days \u2192 months \u2192 years) with past/future/weekend constraints.">
|
|
1094
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
|
1095
|
+
<DatePicker label="Default" placeholder="DD/MM/YYYY" />
|
|
1096
|
+
<DatePicker label="No future dates" disableFuture helperText="Past dates only" />
|
|
1097
|
+
<DatePicker label="Weekdays only" excludeWeekends helperText="Weekends disabled" />
|
|
1098
|
+
</div>
|
|
1099
|
+
</Section>
|
|
1100
|
+
|
|
1101
|
+
{/* \u2500\u2500 Progress \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1102
|
+
<Section id="progress" title="Progress" subtitle="Progress bars in 4 semantic variants and 3 sizes.">
|
|
1103
|
+
<Card className="p-5 space-y-4">
|
|
1104
|
+
<Progress label="Default (68%)" value={68} showValue />
|
|
1105
|
+
<Progress label="Success (90%)" value={90} variant="success" showValue />
|
|
1106
|
+
<Progress label="Warning (45%)" value={45} variant="warning" showValue />
|
|
1107
|
+
<Progress label="Danger (15%)" value={15} variant="danger" showValue />
|
|
1108
|
+
<Separator label="sizes" />
|
|
1109
|
+
<Progress label="Large bar" value={60} size="lg" showValue />
|
|
1110
|
+
<Progress label="Small bar" value={60} size="sm" showValue />
|
|
1111
|
+
</Card>
|
|
1112
|
+
</Section>
|
|
1113
|
+
|
|
1114
|
+
{/* \u2500\u2500 Skeleton \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1115
|
+
<Section id="skeleton" title="Skeleton" subtitle="Loading placeholders while async content fetches.">
|
|
1116
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
|
1117
|
+
<SkeletonCard />
|
|
1118
|
+
<SkeletonCard />
|
|
1119
|
+
<Card className="p-5 space-y-3">
|
|
1120
|
+
<div className="flex items-center gap-3">
|
|
1121
|
+
<Skeleton className="h-12 w-12" rounded="full" />
|
|
1122
|
+
<div className="flex-1 space-y-2">
|
|
1123
|
+
<Skeleton className="h-4" width="75%" />
|
|
1124
|
+
<Skeleton className="h-3" width="50%" />
|
|
1125
|
+
</div>
|
|
1126
|
+
</div>
|
|
1127
|
+
<Skeleton className="h-28 w-full" rounded="lg" />
|
|
1128
|
+
<SkeletonText lines={2} />
|
|
1129
|
+
</Card>
|
|
1130
|
+
</div>
|
|
1131
|
+
</Section>
|
|
1132
|
+
|
|
1133
|
+
{/* \u2500\u2500 Separator \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1134
|
+
<Section id="separator" title="Separator" subtitle="Horizontal, vertical, and labelled dividers.">
|
|
1135
|
+
<Card className="p-5 space-y-4">
|
|
1136
|
+
<Separator />
|
|
1137
|
+
<Separator label="OR" />
|
|
1138
|
+
<div className="flex items-center gap-4 h-8">
|
|
1139
|
+
<span className="text-callout text-label-secondary">Section A</span>
|
|
1140
|
+
<Separator orientation="vertical" />
|
|
1141
|
+
<span className="text-callout text-label-secondary">Section B</span>
|
|
1142
|
+
<Separator orientation="vertical" />
|
|
1143
|
+
<span className="text-callout text-label-secondary">Section C</span>
|
|
1144
|
+
</div>
|
|
1145
|
+
</Card>
|
|
1146
|
+
</Section>
|
|
1147
|
+
|
|
1148
|
+
{/* \u2500\u2500 Tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1149
|
+
<Section id="tooltip" title="Tooltip" subtitle="Hover hints in 4 placement directions.">
|
|
1150
|
+
<Card className="p-5">
|
|
1151
|
+
<Row>
|
|
1152
|
+
<Tooltip content="Appears on top" placement="top"> <Button variant="outline" size="sm">Top</Button> </Tooltip>
|
|
1153
|
+
<Tooltip content="Appears on bottom" placement="bottom"> <Button variant="outline" size="sm">Bottom</Button> </Tooltip>
|
|
1154
|
+
<Tooltip content="Appears on left" placement="left"> <Button variant="outline" size="sm">Left</Button> </Tooltip>
|
|
1155
|
+
<Tooltip content="Appears on right" placement="right"> <Button variant="outline" size="sm">Right</Button> </Tooltip>
|
|
1156
|
+
</Row>
|
|
1157
|
+
</Card>
|
|
1158
|
+
</Section>
|
|
1159
|
+
|
|
1160
|
+
{/* \u2500\u2500 Dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1161
|
+
<Section id="dialog" title="Dialog" subtitle="Modal overlays for forms and confirmations.">
|
|
1162
|
+
<Card className="p-5">
|
|
1163
|
+
<Row>
|
|
1164
|
+
<Button variant="primary" onClick={() => setDialogOpen(true)}>Open form dialog</Button>
|
|
1165
|
+
<Button variant="danger" onClick={() => setConfirmOpen(true)}>Open confirm dialog</Button>
|
|
1166
|
+
</Row>
|
|
1167
|
+
</Card>
|
|
1168
|
+
|
|
1169
|
+
<Dialog
|
|
1170
|
+
open={dialogOpen}
|
|
1171
|
+
onClose={() => setDialogOpen(false)}
|
|
1172
|
+
title="Edit profile"
|
|
1173
|
+
description="Update your name and role."
|
|
1174
|
+
size="md"
|
|
1175
|
+
footer={
|
|
1176
|
+
<>
|
|
1177
|
+
<Button variant="ghost" onClick={() => setDialogOpen(false)}>Cancel</Button>
|
|
1178
|
+
<Button variant="primary" onClick={() => setDialogOpen(false)}>Save changes</Button>
|
|
1179
|
+
</>
|
|
1180
|
+
}
|
|
1181
|
+
>
|
|
1182
|
+
<div className="space-y-4">
|
|
1183
|
+
<Input label="Full name" defaultValue="Priya Mehta" />
|
|
1184
|
+
<Select label="Role" options={SELECT_OPTS} value={selectVal} onChange={setSelectVal} />
|
|
1185
|
+
<Textarea label="Bio" placeholder="Tell us about yourself\u2026" />
|
|
1186
|
+
</div>
|
|
1187
|
+
</Dialog>
|
|
1188
|
+
|
|
1189
|
+
<Dialog
|
|
1190
|
+
open={confirmOpen}
|
|
1191
|
+
onClose={() => setConfirmOpen(false)}
|
|
1192
|
+
title="Delete record"
|
|
1193
|
+
description="This will permanently delete the record. This cannot be undone."
|
|
1194
|
+
size="sm"
|
|
1195
|
+
footer={
|
|
1196
|
+
<>
|
|
1197
|
+
<Button variant="ghost" onClick={() => setConfirmOpen(false)}>Cancel</Button>
|
|
1198
|
+
<Button variant="danger" onClick={() => setConfirmOpen(false)}>Delete</Button>
|
|
1199
|
+
</>
|
|
1200
|
+
}
|
|
1201
|
+
/>
|
|
1202
|
+
</Section>
|
|
1203
|
+
|
|
1204
|
+
{/* \u2500\u2500 Tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1205
|
+
<Section id="tabs" title="Tabs" subtitle="Three visual variants: line, pill, card.">
|
|
1206
|
+
<div className="flex gap-2 mb-3">
|
|
1207
|
+
{(['line', 'pill', 'card'] as const).map((v) => (
|
|
1208
|
+
<Button key={v} variant={tabVariant === v ? 'primary' : 'outline'} size="sm"
|
|
1209
|
+
onClick={() => setTabVariant(v)}>
|
|
1210
|
+
{v}
|
|
1211
|
+
</Button>
|
|
1212
|
+
))}
|
|
1213
|
+
</div>
|
|
1214
|
+
<Card className="p-5">
|
|
1215
|
+
<Tabs defaultValue="overview" variant={tabVariant}>
|
|
1216
|
+
<TabsList>
|
|
1217
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
1218
|
+
<TabsTrigger value="analytics">Analytics</TabsTrigger>
|
|
1219
|
+
<TabsTrigger value="settings">Settings</TabsTrigger>
|
|
1220
|
+
<TabsTrigger value="disabled" disabled>Disabled</TabsTrigger>
|
|
1221
|
+
</TabsList>
|
|
1222
|
+
<TabsContent value="overview">
|
|
1223
|
+
<AlertBanner variant="info">Viewing the Overview tab.</AlertBanner>
|
|
1224
|
+
</TabsContent>
|
|
1225
|
+
<TabsContent value="analytics">
|
|
1226
|
+
<AlertBanner variant="success">Analytics data would appear here.</AlertBanner>
|
|
1227
|
+
</TabsContent>
|
|
1228
|
+
<TabsContent value="settings">
|
|
1229
|
+
<div className="space-y-4">
|
|
1230
|
+
<Switch label="Dark mode" description="Toggle dark theme" />
|
|
1231
|
+
<Switch label="Notifications" defaultChecked />
|
|
1232
|
+
</div>
|
|
1233
|
+
</TabsContent>
|
|
1234
|
+
</Tabs>
|
|
1235
|
+
</Card>
|
|
1236
|
+
</Section>
|
|
1237
|
+
|
|
1238
|
+
{/* \u2500\u2500 Accordion \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1239
|
+
<Section id="accordion" title="Accordion" subtitle="Collapsible FAQ / info panels.">
|
|
1240
|
+
<Card className="px-5">
|
|
1241
|
+
<Accordion type="single" defaultValue="q1">
|
|
1242
|
+
{ACCORDION_ITEMS.map((item) => (
|
|
1243
|
+
<AccordionItem key={item.value} value={item.value} trigger={item.trigger}>
|
|
1244
|
+
{item.body}
|
|
1245
|
+
</AccordionItem>
|
|
1246
|
+
))}
|
|
1247
|
+
</Accordion>
|
|
1248
|
+
</Card>
|
|
1249
|
+
</Section>
|
|
1250
|
+
|
|
1251
|
+
{/* \u2500\u2500 LoadingSpinner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1252
|
+
<Section id="spinner" title="LoadingSpinner" subtitle="Composable spinner in 4 sizes.">
|
|
1253
|
+
<Card className="p-5">
|
|
1254
|
+
<Row>
|
|
1255
|
+
<LoadingSpinner size="sm" />
|
|
1256
|
+
<LoadingSpinner size="md" />
|
|
1257
|
+
<LoadingSpinner size="lg" />
|
|
1258
|
+
<LoadingSpinner size="xl" />
|
|
1259
|
+
<Button variant="primary" isLoading>Loading state</Button>
|
|
1260
|
+
</Row>
|
|
1261
|
+
</Card>
|
|
1262
|
+
</Section>
|
|
1263
|
+
|
|
1264
|
+
{/* \u2500\u2500 DataTable \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1265
|
+
<Section id="datatable" title="DataTable & ActionButtons" subtitle="Typed table with search, skeleton loading, and row actions.">
|
|
1266
|
+
<Card className="overflow-hidden">
|
|
1267
|
+
<DataTable
|
|
1268
|
+
columns={[
|
|
1269
|
+
{ key: 'name', header: 'Name', render: (r: {name: string}) => <span className="font-medium text-label-primary">{r.name}</span> },
|
|
1270
|
+
{ key: 'role', header: 'Role' },
|
|
1271
|
+
{ key: 'status', header: 'Status', render: (r: {status: string}) => <StatusBadge status={r.status} /> },
|
|
1272
|
+
{ key: 'joined', header: 'Joined', render: (r: {joined: string}) => <span className="text-label-tertiary">{r.joined}</span> },
|
|
1273
|
+
{ key: 'actions', header: '', render: () => <ActionButtons showView showEdit onView={() => {}} onEdit={() => {}} /> },
|
|
1274
|
+
]}
|
|
1275
|
+
data={TABLE_DATA}
|
|
1276
|
+
keyExtractor={(r: {id: number}) => r.id}
|
|
1277
|
+
searchable
|
|
1278
|
+
searchPlaceholder="Search members\u2026"
|
|
1279
|
+
/>
|
|
1280
|
+
</Card>
|
|
1281
|
+
</Section>
|
|
1282
|
+
|
|
1283
|
+
{/* \u2500\u2500 Charts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1284
|
+
<Section id="charts" title="Charts" subtitle="Bar, line, area, donut \u2014 built on recharts (npm install recharts).">
|
|
1285
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
|
1286
|
+
<Card className="p-5">
|
|
1287
|
+
<p className="text-callout font-semibold text-label-primary mb-4">PortalBarChart</p>
|
|
1288
|
+
<PortalBarChart
|
|
1289
|
+
data={BAR_DATA}
|
|
1290
|
+
xKey="month"
|
|
1291
|
+
series={[{ key: 'revenue', name: 'Revenue' }, { key: 'expenses', name: 'Expenses' }]}
|
|
1292
|
+
height={200}
|
|
1293
|
+
/>
|
|
1294
|
+
</Card>
|
|
1295
|
+
<Card className="p-5">
|
|
1296
|
+
<p className="text-callout font-semibold text-label-primary mb-4">PortalDonutChart</p>
|
|
1297
|
+
<PortalDonutChart data={DONUT_DATA} centerLabel="Total" centerValue={100} height={200} />
|
|
1298
|
+
</Card>
|
|
1299
|
+
<Card className="p-5">
|
|
1300
|
+
<p className="text-callout font-semibold text-label-primary mb-4">PortalLineChart</p>
|
|
1301
|
+
<PortalLineChart
|
|
1302
|
+
data={BAR_DATA}
|
|
1303
|
+
xKey="month"
|
|
1304
|
+
series={[{ key: 'revenue', name: 'Revenue' }]}
|
|
1305
|
+
height={200}
|
|
1306
|
+
/>
|
|
1307
|
+
</Card>
|
|
1308
|
+
<Card className="p-5">
|
|
1309
|
+
<p className="text-callout font-semibold text-label-primary mb-4">PortalAreaChart</p>
|
|
1310
|
+
<PortalAreaChart
|
|
1311
|
+
data={BAR_DATA}
|
|
1312
|
+
xKey="month"
|
|
1313
|
+
series={[{ key: 'revenue', name: 'Revenue' }]}
|
|
1314
|
+
height={200}
|
|
1315
|
+
/>
|
|
1316
|
+
</Card>
|
|
1317
|
+
</div>
|
|
1318
|
+
</Section>
|
|
1319
|
+
|
|
1320
|
+
{/* \u2500\u2500 TricolorBar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */}
|
|
1321
|
+
<Section id="tricolor" title="TricolorBar" subtitle="Brand accent bar \u2014 appears in sidebar header/footer and login card.">
|
|
1322
|
+
<TricolorBar />
|
|
1323
|
+
</Section>
|
|
1324
|
+
|
|
1325
|
+
</div>
|
|
1326
|
+
)
|
|
1327
|
+
}
|
|
1328
|
+
`;
|
|
1329
|
+
}
|
|
836
1330
|
|
|
837
1331
|
// src/cli/index.ts
|
|
838
1332
|
function write(filePath, content) {
|
|
@@ -1018,6 +1512,7 @@ export default config
|
|
|
1018
1512
|
write(f("src/app/dashboard/page.tsx"), genDashboardHomePage(opts));
|
|
1019
1513
|
write(f("src/app/dashboard/users/page.tsx"), genUsersPage(opts));
|
|
1020
1514
|
write(f("src/app/dashboard/settings/page.tsx"), genSettingsPage(opts));
|
|
1515
|
+
write(f("src/app/dashboard/components/page.tsx"), genComponentsShowcasePage());
|
|
1021
1516
|
write(f("src/app/api/auth/login/route.ts"), genLoginRoute(opts));
|
|
1022
1517
|
write(f("src/app/api/auth/user/route.ts"), genUserRoute(opts));
|
|
1023
1518
|
write(f("src/app/api/auth/session/route.ts"), genSessionRoute(opts));
|
package/package.json
CHANGED