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.
Files changed (49) hide show
  1. package/README.md +94 -0
  2. package/bin/create-mercato-app +21 -0
  3. package/dist/index.js +177 -0
  4. package/package.json +42 -0
  5. package/template/.env.example +217 -0
  6. package/template/.yarnrc.yml.template +2 -0
  7. package/template/components.json +22 -0
  8. package/template/gitignore +50 -0
  9. package/template/next.config.ts +28 -0
  10. package/template/package.json.template +87 -0
  11. package/template/postcss.config.mjs +5 -0
  12. package/template/public/catch-the-tornado-logo.png +0 -0
  13. package/template/public/file.svg +1 -0
  14. package/template/public/globe.svg +1 -0
  15. package/template/public/next.svg +1 -0
  16. package/template/public/open-mercato.svg +50 -0
  17. package/template/public/vercel.svg +1 -0
  18. package/template/public/window.svg +1 -0
  19. package/template/src/app/(backend)/backend/[...slug]/page.tsx +59 -0
  20. package/template/src/app/(backend)/backend/layout.tsx +350 -0
  21. package/template/src/app/(backend)/backend/page.tsx +13 -0
  22. package/template/src/app/(frontend)/[...slug]/page.tsx +32 -0
  23. package/template/src/app/api/[...slug]/route.ts +227 -0
  24. package/template/src/app/api/docs/markdown/route.ts +35 -0
  25. package/template/src/app/api/docs/openapi/route.ts +30 -0
  26. package/template/src/app/globals.css +178 -0
  27. package/template/src/app/layout.tsx +76 -0
  28. package/template/src/app/page.tsx +134 -0
  29. package/template/src/bootstrap.ts +58 -0
  30. package/template/src/components/ClientBootstrap.tsx +37 -0
  31. package/template/src/components/GlobalNoticeBars.tsx +116 -0
  32. package/template/src/components/OrganizationSwitcher.tsx +360 -0
  33. package/template/src/components/StartPageContent.tsx +269 -0
  34. package/template/src/components/ui/button.tsx +59 -0
  35. package/template/src/components/ui/card.tsx +92 -0
  36. package/template/src/components/ui/checkbox.tsx +29 -0
  37. package/template/src/components/ui/input.tsx +21 -0
  38. package/template/src/components/ui/label.tsx +24 -0
  39. package/template/src/di.ts +11 -0
  40. package/template/src/i18n/de.json +375 -0
  41. package/template/src/i18n/en.json +376 -0
  42. package/template/src/i18n/es.json +376 -0
  43. package/template/src/i18n/pl.json +375 -0
  44. package/template/src/modules/.gitkeep +0 -0
  45. package/template/src/modules.ts +31 -0
  46. package/template/src/proxy.ts +17 -0
  47. package/template/tsconfig.json +54 -0
  48. package/template/types/pg/index.d.ts +1 -0
  49. 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 &lt;email&gt; --password &lt;newPassword&gt;</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
+ }