create-mercato-app 0.4.2-canary-e5804f7db1
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/README.md +94 -0
- package/bin/create-mercato-app +21 -0
- package/dist/index.js +177 -0
- package/package.json +42 -0
- package/template/.env.example +217 -0
- package/template/.yarnrc.yml.template +2 -0
- package/template/components.json +22 -0
- package/template/gitignore +50 -0
- package/template/next.config.ts +28 -0
- package/template/package.json.template +87 -0
- package/template/postcss.config.mjs +5 -0
- package/template/public/catch-the-tornado-logo.png +0 -0
- package/template/public/file.svg +1 -0
- package/template/public/globe.svg +1 -0
- package/template/public/next.svg +1 -0
- package/template/public/open-mercato.svg +50 -0
- package/template/public/vercel.svg +1 -0
- package/template/public/window.svg +1 -0
- package/template/src/app/(backend)/backend/[...slug]/page.tsx +59 -0
- package/template/src/app/(backend)/backend/layout.tsx +350 -0
- package/template/src/app/(backend)/backend/page.tsx +13 -0
- package/template/src/app/(frontend)/[...slug]/page.tsx +32 -0
- package/template/src/app/api/[...slug]/route.ts +227 -0
- package/template/src/app/api/docs/markdown/route.ts +35 -0
- package/template/src/app/api/docs/openapi/route.ts +30 -0
- package/template/src/app/globals.css +178 -0
- package/template/src/app/layout.tsx +76 -0
- package/template/src/app/page.tsx +134 -0
- package/template/src/bootstrap.ts +58 -0
- package/template/src/components/ClientBootstrap.tsx +37 -0
- package/template/src/components/GlobalNoticeBars.tsx +116 -0
- package/template/src/components/OrganizationSwitcher.tsx +360 -0
- package/template/src/components/StartPageContent.tsx +269 -0
- package/template/src/components/ui/button.tsx +59 -0
- package/template/src/components/ui/card.tsx +92 -0
- package/template/src/components/ui/checkbox.tsx +29 -0
- package/template/src/components/ui/input.tsx +21 -0
- package/template/src/components/ui/label.tsx +24 -0
- package/template/src/di.ts +11 -0
- package/template/src/i18n/de.json +375 -0
- package/template/src/i18n/en.json +376 -0
- package/template/src/i18n/es.json +376 -0
- package/template/src/i18n/pl.json +375 -0
- package/template/src/modules/.gitkeep +0 -0
- package/template/src/modules.ts +31 -0
- package/template/src/proxy.ts +17 -0
- package/template/tsconfig.json +54 -0
- package/template/types/pg/index.d.ts +1 -0
- package/template/types/react-big-calendar/index.d.ts +16 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState, type ReactNode } from 'react'
|
|
4
|
+
import { Button } from '@/components/ui/button'
|
|
5
|
+
import { Checkbox } from '@/components/ui/checkbox'
|
|
6
|
+
import { Shield, Users, Briefcase, Info, Rocket, ArrowRight, BookOpen } from 'lucide-react'
|
|
7
|
+
import { getApiDocsResources, resolveApiDocsBaseUrl } from '@open-mercato/core/modules/api_docs/lib/resources'
|
|
8
|
+
import Link from 'next/link'
|
|
9
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
10
|
+
|
|
11
|
+
interface RoleTileProps {
|
|
12
|
+
icon: ReactNode
|
|
13
|
+
title: string
|
|
14
|
+
description: string
|
|
15
|
+
features: string[]
|
|
16
|
+
loginUrl: string
|
|
17
|
+
variant?: 'default' | 'secondary' | 'outline'
|
|
18
|
+
disabled?: boolean
|
|
19
|
+
disabledCtaLabel?: string
|
|
20
|
+
disabledMessage?: ReactNode
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function RoleTile({
|
|
24
|
+
icon,
|
|
25
|
+
title,
|
|
26
|
+
description,
|
|
27
|
+
features,
|
|
28
|
+
loginUrl,
|
|
29
|
+
variant = 'default',
|
|
30
|
+
disabled = false,
|
|
31
|
+
disabledCtaLabel,
|
|
32
|
+
disabledMessage,
|
|
33
|
+
}: RoleTileProps) {
|
|
34
|
+
const t = useT()
|
|
35
|
+
const defaultDisabledCtaLabel = t('startPage.roleTile.loginUnavailable', 'Login unavailable')
|
|
36
|
+
return (
|
|
37
|
+
<div className="rounded-lg border bg-card p-6 flex flex-col gap-4 transition-all hover:shadow-md">
|
|
38
|
+
<div className="flex items-start gap-4">
|
|
39
|
+
<div className="rounded-lg bg-primary/10 p-3 text-primary">
|
|
40
|
+
{icon}
|
|
41
|
+
</div>
|
|
42
|
+
<div className="flex-1">
|
|
43
|
+
<h3 className="text-lg font-semibold">{title}</h3>
|
|
44
|
+
<p className="text-sm text-muted-foreground mt-1">{description}</p>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div className="flex-1">
|
|
49
|
+
<div className="text-xs font-medium text-muted-foreground mb-2">{t('startPage.roleTile.availableFeatures', 'Available Features:')}</div>
|
|
50
|
+
<ul className="space-y-1.5">
|
|
51
|
+
{features.map((feature, idx) => (
|
|
52
|
+
<li key={idx} className="text-sm flex items-start gap-2">
|
|
53
|
+
<span className="text-primary mt-0.5">•</span>
|
|
54
|
+
<span>{feature}</span>
|
|
55
|
+
</li>
|
|
56
|
+
))}
|
|
57
|
+
</ul>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
{disabled ? (
|
|
61
|
+
<>
|
|
62
|
+
<Button variant="outline" className="w-full cursor-not-allowed opacity-80" disabled>
|
|
63
|
+
{disabledCtaLabel ?? defaultDisabledCtaLabel}
|
|
64
|
+
</Button>
|
|
65
|
+
{disabledMessage ? (
|
|
66
|
+
<p className="text-xs text-muted-foreground text-center leading-relaxed">
|
|
67
|
+
{disabledMessage}
|
|
68
|
+
</p>
|
|
69
|
+
) : null}
|
|
70
|
+
</>
|
|
71
|
+
) : (
|
|
72
|
+
<Button asChild variant={variant} className="w-full">
|
|
73
|
+
<Link href={loginUrl}>{t('startPage.roleTile.loginAs', 'Login as {title}', { title })}</Link>
|
|
74
|
+
</Button>
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface StartPageContentProps {
|
|
81
|
+
showStartPage: boolean
|
|
82
|
+
showOnboardingCta?: boolean
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function StartPageContent({ showStartPage: initialShowStartPage, showOnboardingCta = false }: StartPageContentProps) {
|
|
86
|
+
const t = useT()
|
|
87
|
+
const [showStartPage, setShowStartPage] = useState(initialShowStartPage)
|
|
88
|
+
|
|
89
|
+
const superAdminDisabled = showOnboardingCta
|
|
90
|
+
const apiDocs = getApiDocsResources()
|
|
91
|
+
const baseUrl = resolveApiDocsBaseUrl()
|
|
92
|
+
|
|
93
|
+
const handleCheckboxChange = (checked: boolean) => {
|
|
94
|
+
setShowStartPage(checked)
|
|
95
|
+
// Set cookie to remember preference
|
|
96
|
+
document.cookie = `show_start_page=${checked}; path=/; max-age=${365 * 24 * 60 * 60}; SameSite=Lax`
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<>
|
|
101
|
+
<section className="rounded-lg border bg-gradient-to-br from-background to-muted/20 p-8 text-center">
|
|
102
|
+
<h2 className="text-2xl font-semibold mb-3">{t('startPage.welcome.title', 'Welcome to Your Open Mercato Installation')}</h2>
|
|
103
|
+
<p className="text-muted-foreground max-w-2xl mx-auto">
|
|
104
|
+
{t('startPage.welcome.description', 'This is a customizable start page for your fresh Open Mercato installation. Choose your role below to get started and explore the features available to you.')}
|
|
105
|
+
</p>
|
|
106
|
+
</section>
|
|
107
|
+
|
|
108
|
+
{showOnboardingCta ? (
|
|
109
|
+
<section className="rounded-lg border border-emerald-300 bg-emerald-50 dark:border-emerald-800 dark:bg-emerald-950/20 p-6 md:p-8 flex flex-col md:flex-row md:items-center gap-6 shadow-sm">
|
|
110
|
+
<div className="flex items-start gap-4">
|
|
111
|
+
<div className="rounded-full bg-emerald-600 text-white p-3">
|
|
112
|
+
<Rocket className="size-6" />
|
|
113
|
+
</div>
|
|
114
|
+
<div className="space-y-3">
|
|
115
|
+
<div>
|
|
116
|
+
<h3 className="text-lg font-semibold text-emerald-900 dark:text-emerald-100">{t('startPage.onboarding.title', 'Launch your own workspace')}</h3>
|
|
117
|
+
<p className="text-sm text-emerald-800/80 dark:text-emerald-200/90">
|
|
118
|
+
{t('startPage.onboarding.description', 'Create a tenant, organization, and administrator account in minutes. We\'ll verify your email and deliver a pre-seeded environment so you can explore Open Mercato with real data.')}
|
|
119
|
+
</p>
|
|
120
|
+
</div>
|
|
121
|
+
<ul className="text-sm text-emerald-900/80 dark:text-emerald-200/90 space-y-1 list-disc pl-5 marker:text-emerald-600 dark:marker:text-emerald-400">
|
|
122
|
+
<li>{t('startPage.onboarding.feature1', 'Automatic tenant and sample data provisioning')}</li>
|
|
123
|
+
<li>{t('startPage.onboarding.feature2', 'Ready-to-use superadmin credentials after verification')}</li>
|
|
124
|
+
</ul>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
<div className="md:ml-auto">
|
|
128
|
+
<Button asChild className="bg-emerald-600 hover:bg-emerald-700 focus-visible:ring-emerald-600 px-6 py-5 text-base font-semibold text-white shadow-md">
|
|
129
|
+
<Link href="/onboarding">
|
|
130
|
+
{t('startPage.onboarding.cta', 'Start onboarding')}
|
|
131
|
+
<ArrowRight className="size-4" aria-hidden />
|
|
132
|
+
</Link>
|
|
133
|
+
</Button>
|
|
134
|
+
</div>
|
|
135
|
+
</section>
|
|
136
|
+
) : null}
|
|
137
|
+
|
|
138
|
+
<section className="rounded-lg border bg-blue-50 dark:bg-blue-950/20 border-blue-200 dark:border-blue-900 p-4">
|
|
139
|
+
<div className="flex items-start gap-3">
|
|
140
|
+
<Info className="size-5 text-blue-600 dark:text-blue-400 shrink-0 mt-0.5" />
|
|
141
|
+
<div className="flex-1">
|
|
142
|
+
<h3 className="text-sm font-semibold text-blue-900 dark:text-blue-100 mb-1">{t('startPage.defaultPassword.title', 'Default Password')}</h3>
|
|
143
|
+
<p className="text-sm text-blue-800 dark:text-blue-200">
|
|
144
|
+
{t('startPage.defaultPassword.description1', 'The default password for all demo accounts is')}{' '}
|
|
145
|
+
<code className="px-1.5 py-0.5 rounded bg-blue-100 dark:bg-blue-900 font-mono text-xs">secret</code>.
|
|
146
|
+
{' '}{t('startPage.defaultPassword.description2', 'To change passwords, use the CLI command:')}{' '}
|
|
147
|
+
<code className="px-1.5 py-0.5 rounded bg-blue-100 dark:bg-blue-900 font-mono text-xs">yarn mercato auth set-password --email <email> --password <newPassword></code>
|
|
148
|
+
</p>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</section>
|
|
152
|
+
|
|
153
|
+
<section>
|
|
154
|
+
<h2 className="text-xl font-semibold mb-4">{t('startPage.chooseRole.title', 'Choose Your Role')}</h2>
|
|
155
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
156
|
+
<RoleTile
|
|
157
|
+
icon={<Shield className="size-6" />}
|
|
158
|
+
title={t('startPage.roles.superAdmin.title', 'Super Admin')}
|
|
159
|
+
description={t('startPage.roles.superAdmin.description', 'Full system access with complete control')}
|
|
160
|
+
features={[
|
|
161
|
+
t('startPage.roles.superAdmin.feature1', 'Manage organization structure'),
|
|
162
|
+
t('startPage.roles.superAdmin.feature2', 'Create and manage roles'),
|
|
163
|
+
t('startPage.roles.superAdmin.feature3', 'Manage all users across organizations'),
|
|
164
|
+
t('startPage.roles.superAdmin.feature4', 'System-wide configuration'),
|
|
165
|
+
t('startPage.roles.superAdmin.feature5', 'Access to all modules and features')
|
|
166
|
+
]}
|
|
167
|
+
loginUrl="/login?role=superadmin"
|
|
168
|
+
disabled={superAdminDisabled}
|
|
169
|
+
disabledCtaLabel={t('startPage.roles.superAdmin.disabledCta', 'Superadmin login disabled')}
|
|
170
|
+
disabledMessage={
|
|
171
|
+
<>
|
|
172
|
+
{t('startPage.roles.superAdmin.disabledMessage1', 'Superadmin demo access is not enabled on this instance.')}{' '}
|
|
173
|
+
{t('startPage.roles.superAdmin.disabledMessage2', 'Install Open Mercato locally for full access via')}{' '}
|
|
174
|
+
<a
|
|
175
|
+
href="https://github.com/open-mercato"
|
|
176
|
+
target="_blank"
|
|
177
|
+
rel="noreferrer"
|
|
178
|
+
className="underline hover:text-primary transition-colors"
|
|
179
|
+
>
|
|
180
|
+
github.com/open-mercato
|
|
181
|
+
</a>
|
|
182
|
+
.
|
|
183
|
+
</>
|
|
184
|
+
}
|
|
185
|
+
/>
|
|
186
|
+
|
|
187
|
+
<RoleTile
|
|
188
|
+
icon={<Users className="size-6" />}
|
|
189
|
+
title={t('startPage.roles.admin.title', 'Admin')}
|
|
190
|
+
description={t('startPage.roles.admin.description', 'Organization-level administration')}
|
|
191
|
+
features={[
|
|
192
|
+
t('startPage.roles.admin.feature1', 'Admin specific organization(s)'),
|
|
193
|
+
t('startPage.roles.admin.feature2', 'Manage users within organization'),
|
|
194
|
+
t('startPage.roles.admin.feature3', 'Configure organization settings'),
|
|
195
|
+
t('startPage.roles.admin.feature4', 'Access to admin modules'),
|
|
196
|
+
t('startPage.roles.admin.feature5', 'Report and analytics access')
|
|
197
|
+
]}
|
|
198
|
+
loginUrl="/login?role=admin"
|
|
199
|
+
variant="secondary"
|
|
200
|
+
/>
|
|
201
|
+
|
|
202
|
+
<RoleTile
|
|
203
|
+
icon={<Briefcase className="size-6" />}
|
|
204
|
+
title={t('startPage.roles.employee.title', 'Employee')}
|
|
205
|
+
description={t('startPage.roles.employee.description', 'Work on your daily tasks')}
|
|
206
|
+
features={[
|
|
207
|
+
t('startPage.roles.employee.feature1', 'Work on assigned tasks'),
|
|
208
|
+
t('startPage.roles.employee.feature2', 'Access organization resources'),
|
|
209
|
+
t('startPage.roles.employee.feature3', 'Collaborate with team members'),
|
|
210
|
+
t('startPage.roles.employee.feature4', 'View personal dashboard'),
|
|
211
|
+
t('startPage.roles.employee.feature5', 'Submit reports and updates')
|
|
212
|
+
]}
|
|
213
|
+
loginUrl="/login?role=employee"
|
|
214
|
+
variant="outline"
|
|
215
|
+
/>
|
|
216
|
+
</div>
|
|
217
|
+
</section>
|
|
218
|
+
|
|
219
|
+
<section className="rounded-lg border bg-card p-6 space-y-4">
|
|
220
|
+
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
|
221
|
+
<div className="flex items-start gap-3">
|
|
222
|
+
<span className="rounded-full bg-primary/10 p-2 text-primary">
|
|
223
|
+
<BookOpen className="size-5" />
|
|
224
|
+
</span>
|
|
225
|
+
<div>
|
|
226
|
+
<h2 className="text-lg font-semibold">{t('startPage.apiResources.title', 'API resources')}</h2>
|
|
227
|
+
<p className="text-sm text-muted-foreground">
|
|
228
|
+
{t('startPage.apiResources.description', 'Explore the official documentation and download the generated OpenAPI exports for this installation.')}
|
|
229
|
+
</p>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
<div className="grid gap-3 md:grid-cols-3">
|
|
234
|
+
{apiDocs.map((resource) => (
|
|
235
|
+
<a
|
|
236
|
+
key={resource.href}
|
|
237
|
+
href={resource.href}
|
|
238
|
+
target={resource.external ? '_blank' : undefined}
|
|
239
|
+
rel={resource.external ? 'noreferrer' : undefined}
|
|
240
|
+
className="rounded border bg-background p-4 text-sm transition hover:border-primary"
|
|
241
|
+
>
|
|
242
|
+
<div className="font-medium text-foreground">{resource.label}</div>
|
|
243
|
+
<p className="mt-1 text-xs text-muted-foreground">{resource.description}</p>
|
|
244
|
+
<span className="mt-3 inline-flex text-xs font-medium text-primary">{resource.actionLabel ?? t('startPage.apiResources.openLink', 'Open link')}</span>
|
|
245
|
+
</a>
|
|
246
|
+
))}
|
|
247
|
+
</div>
|
|
248
|
+
<p className="text-xs text-muted-foreground">
|
|
249
|
+
{t('startPage.apiResources.baseUrl', 'Current API base URL:')}{' '}
|
|
250
|
+
<code className="rounded bg-muted px-2 py-0.5 text-[10px] text-foreground">{baseUrl}</code>
|
|
251
|
+
</p>
|
|
252
|
+
</section>
|
|
253
|
+
|
|
254
|
+
<section className="rounded-lg border p-4 flex items-center justify-center gap-3">
|
|
255
|
+
<Checkbox
|
|
256
|
+
id="show-start-page"
|
|
257
|
+
checked={showStartPage}
|
|
258
|
+
onCheckedChange={handleCheckboxChange}
|
|
259
|
+
/>
|
|
260
|
+
<label
|
|
261
|
+
htmlFor="show-start-page"
|
|
262
|
+
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer"
|
|
263
|
+
>
|
|
264
|
+
{t('startPage.showNextTime', 'Display this start page next time')}
|
|
265
|
+
</label>
|
|
266
|
+
</section>
|
|
267
|
+
</>
|
|
268
|
+
)
|
|
269
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@open-mercato/shared/lib/utils"
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
16
|
+
outline:
|
|
17
|
+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
18
|
+
secondary:
|
|
19
|
+
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
20
|
+
ghost:
|
|
21
|
+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
22
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
26
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
27
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
28
|
+
icon: "size-9",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: {
|
|
32
|
+
variant: "default",
|
|
33
|
+
size: "default",
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
function Button({
|
|
39
|
+
className,
|
|
40
|
+
variant,
|
|
41
|
+
size,
|
|
42
|
+
asChild = false,
|
|
43
|
+
...props
|
|
44
|
+
}: React.ComponentProps<"button"> &
|
|
45
|
+
VariantProps<typeof buttonVariants> & {
|
|
46
|
+
asChild?: boolean
|
|
47
|
+
}) {
|
|
48
|
+
const Comp = asChild ? Slot : "button"
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Comp
|
|
52
|
+
data-slot="button"
|
|
53
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { Button, buttonVariants }
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@open-mercato/shared/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
data-slot="card"
|
|
9
|
+
className={cn(
|
|
10
|
+
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
data-slot="card-header"
|
|
22
|
+
className={cn(
|
|
23
|
+
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
data-slot="card-title"
|
|
35
|
+
className={cn("leading-none font-semibold", className)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
data-slot="card-description"
|
|
45
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
data-slot="card-action"
|
|
55
|
+
className={cn(
|
|
56
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
data-slot="card-content"
|
|
68
|
+
className={cn("px-6", className)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
75
|
+
return (
|
|
76
|
+
<div
|
|
77
|
+
data-slot="card-footer"
|
|
78
|
+
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
Card,
|
|
86
|
+
CardHeader,
|
|
87
|
+
CardFooter,
|
|
88
|
+
CardTitle,
|
|
89
|
+
CardAction,
|
|
90
|
+
CardDescription,
|
|
91
|
+
CardContent,
|
|
92
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
|
3
|
+
import { Check } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@open-mercato/shared/lib/utils"
|
|
6
|
+
|
|
7
|
+
const Checkbox = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
|
10
|
+
>(({ className, ...props }, ref) => (
|
|
11
|
+
<CheckboxPrimitive.Root
|
|
12
|
+
ref={ref}
|
|
13
|
+
className={cn(
|
|
14
|
+
"peer size-4 shrink-0 rounded-sm border border-input bg-background shadow-xs ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
>
|
|
19
|
+
<CheckboxPrimitive.Indicator
|
|
20
|
+
className={cn("flex items-center justify-center text-current")}
|
|
21
|
+
>
|
|
22
|
+
<Check className="size-3.5" />
|
|
23
|
+
</CheckboxPrimitive.Indicator>
|
|
24
|
+
</CheckboxPrimitive.Root>
|
|
25
|
+
))
|
|
26
|
+
Checkbox.displayName = CheckboxPrimitive.Root.displayName
|
|
27
|
+
|
|
28
|
+
export { Checkbox }
|
|
29
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@open-mercato/shared/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
6
|
+
return (
|
|
7
|
+
<input
|
|
8
|
+
type={type}
|
|
9
|
+
data-slot="input"
|
|
10
|
+
className={cn(
|
|
11
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
13
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { Input }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as LabelPrimitive from "@radix-ui/react-label"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@open-mercato/shared/lib/utils"
|
|
7
|
+
|
|
8
|
+
function Label({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
|
12
|
+
return (
|
|
13
|
+
<LabelPrimitive.Root
|
|
14
|
+
data-slot="label"
|
|
15
|
+
className={cn(
|
|
16
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { Label }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AppContainer } from '@open-mercato/shared/lib/di/container'
|
|
2
|
+
import { bootstrap } from '@open-mercato/core/bootstrap'
|
|
3
|
+
|
|
4
|
+
// App-level DI overrides/registrations.
|
|
5
|
+
// This runs after core defaults and module DI registrars.
|
|
6
|
+
export async function register(container: AppContainer) {
|
|
7
|
+
// Call core bootstrap to setup eventBus and auto-register subscribers
|
|
8
|
+
// Feel free to remove or customize this for your app needs
|
|
9
|
+
await bootstrap(container)
|
|
10
|
+
// App-level overrides can follow here
|
|
11
|
+
}
|