create-mercato-app 0.6.6-develop.6176.1.4507b99c2f → 0.6.6-develop.6198.1.0822f97cc6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-mercato-app",
3
- "version": "0.6.6-develop.6176.1.4507b99c2f",
3
+ "version": "0.6.6-develop.6198.1.0822f97cc6",
4
4
  "type": "module",
5
5
  "description": "Create a new Open Mercato application",
6
6
  "main": "./dist/index.js",
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'
4
4
  import Link from 'next/link'
5
5
  import { X } from 'lucide-react'
6
6
  import { Button } from '@/components/ui/button'
7
+ import { IconButton } from '@open-mercato/ui/primitives/icon-button'
7
8
  import { useT } from '@open-mercato/shared/lib/i18n/context'
8
9
 
9
10
  const DEMO_NOTICE_COOKIE = 'om_demo_notice_ack'
@@ -54,11 +55,11 @@ export function GlobalNoticeBars({ demoModeEnabled }: { demoModeEnabled: boolean
54
55
  }
55
56
 
56
57
  return (
57
- <div className="pointer-events-none fixed inset-x-0 bottom-4 z-[70] flex flex-col items-center gap-3 px-4">
58
+ <div className="pointer-events-none fixed inset-x-0 bottom-4 z-banner flex flex-col items-center gap-3 px-4">
58
59
  {showDemoNotice ? (
59
- <div className="pointer-events-auto w-full max-w-4xl rounded-lg border border-amber-200 bg-amber-50/90 p-4 shadow-lg backdrop-blur supports-[backdrop-filter]:bg-amber-50/70 dark:border-amber-900/70 dark:bg-amber-950/40">
60
+ <div className="pointer-events-auto w-full max-w-4xl rounded-lg border border-status-warning-border bg-status-warning-bg/90 p-4 shadow-lg backdrop-blur supports-[backdrop-filter]:bg-status-warning-bg/80">
60
61
  <div className="flex items-start gap-3">
61
- <div className="flex-1 text-sm text-amber-900 dark:text-amber-50 space-y-1">
62
+ <div className="flex-1 text-sm text-status-warning-text space-y-1">
62
63
  <p className="font-medium">{t('notices.demo.title', 'Demo Environment')}</p>
63
64
  <p>
64
65
  {t('notices.demo.description', 'This instance is provided for demo purposes only. Data may be reset at any time and is not retained for any guaranteed period.')}
@@ -69,30 +70,37 @@ export function GlobalNoticeBars({ demoModeEnabled }: { demoModeEnabled: boolean
69
70
  href="https://github.com/open-mercato"
70
71
  target="_blank"
71
72
  rel="noreferrer"
72
- className="underline font-medium hover:text-amber-800 dark:hover:text-amber-200"
73
+ className="underline font-medium hover:text-status-warning-icon"
73
74
  >
74
75
  {t('notices.demo.installLink', 'Install Open Mercato locally')}
75
76
  </a>
76
77
  . {t('notices.demo.reviewLinks', 'Review our')}{' '}
77
- <Link className="underline font-medium hover:text-amber-800 dark:hover:text-amber-200" href="/terms">
78
+ <Link className="underline font-medium hover:text-status-warning-icon" href="/terms">
78
79
  {t('common.terms')}
79
80
  </Link>{' '}
80
81
  {t('notices.demo.and', 'and')}{' '}
81
- <Link className="underline font-medium hover:text-amber-800 dark:hover:text-amber-200" href="/privacy">
82
+ <Link className="underline font-medium hover:text-status-warning-icon" href="/privacy">
82
83
  {t('common.privacy')}
83
84
  </Link>
84
85
  .
85
86
  </p>
86
87
  </div>
87
- <Button variant="ghost" size="icon" onClick={handleDismissDemo} className="shrink-0 text-amber-900 dark:text-amber-100">
88
+ <IconButton
89
+ type="button"
90
+ variant="ghost"
91
+ size="lg"
92
+ onClick={handleDismissDemo}
93
+ className="shrink-0 text-status-warning-text hover:text-status-warning-icon"
94
+ aria-label={t('notices.demo.dismiss', 'Dismiss demo notice')}
95
+ >
88
96
  <X className="size-4" />
89
- </Button>
97
+ </IconButton>
90
98
  </div>
91
99
  </div>
92
100
  ) : null}
93
101
 
94
102
  {showCookieNotice ? (
95
- <div className="pointer-events-auto w-full max-w-4xl rounded-lg border border-slate-300 bg-background/95 p-4 shadow-lg backdrop-blur supports-[backdrop-filter]:bg-background/80 dark:border-slate-700">
103
+ <div className="pointer-events-auto w-full max-w-4xl rounded-lg border border-border bg-background/95 p-4 shadow-lg backdrop-blur supports-[backdrop-filter]:bg-background/80">
96
104
  <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
97
105
  <div className="text-sm text-muted-foreground">
98
106
  {t('notices.cookies.description', 'We use essential cookies to remember your preferences. Learn how we handle data in our')}{' '}
@@ -101,10 +109,10 @@ export function GlobalNoticeBars({ demoModeEnabled }: { demoModeEnabled: boolean
101
109
  </Link>.
102
110
  </div>
103
111
  <div className="flex items-center gap-2 self-end sm:self-auto">
104
- <Button variant="ghost" size="sm" onClick={handleDismissCookies}>
112
+ <Button type="button" variant="ghost" size="sm" onClick={handleDismissCookies}>
105
113
  {t('notices.cookies.dismiss', 'Dismiss')}
106
114
  </Button>
107
- <Button size="sm" onClick={handleAcceptCookies}>
115
+ <Button type="button" size="sm" onClick={handleAcceptCookies}>
108
116
  {t('notices.cookies.accept', 'Accept cookies')}
109
117
  </Button>
110
118
  </div>
@@ -20,6 +20,7 @@ import {
20
20
  PopoverContent,
21
21
  PopoverTrigger,
22
22
  } from '@open-mercato/ui/primitives/popover'
23
+ import { Button } from '@open-mercato/ui/primitives/button'
23
24
  import { ALL_ORGANIZATIONS_COOKIE_VALUE } from '@open-mercato/core/modules/directory/constants'
24
25
  import { useT } from '@open-mercato/shared/lib/i18n/context'
25
26
 
@@ -331,6 +332,37 @@ export default function OrganizationSwitcher({ compact }: OrganizationSwitcherEx
331
332
  : tenantValue
332
333
  const showTenantSelect = state.status === 'ready' && state.isSuperAdmin && tenantSelectOptions.length > 0
333
334
 
335
+ const flatOrgOptions = React.useMemo(() => {
336
+ const out: Array<{ id: string; label: string; selectable: boolean; depth: number }> = []
337
+ const walk = (list: OrganizationTreeNode[]) => {
338
+ for (const node of list) {
339
+ const depth = typeof node.depth === 'number' ? node.depth : 0
340
+ const indent = depth > 0 ? `${'  '.repeat(depth)}` : ''
341
+ out.push({
342
+ id: node.id,
343
+ label: `${indent}${node.name}`,
344
+ selectable: node.selectable !== false,
345
+ depth,
346
+ })
347
+ if (Array.isArray(node.children) && node.children.length > 0) walk(node.children as OrganizationTreeNode[])
348
+ }
349
+ }
350
+ walk(nodes)
351
+ return out
352
+ }, [nodes])
353
+
354
+ const [popoverOpen, setPopoverOpen] = React.useState(false)
355
+
356
+ const activeOrgLabel = React.useMemo(() => {
357
+ if (!value) {
358
+ return showAllOption
359
+ ? t('organizationSwitcher.allOrganizations', 'All organizations')
360
+ : t('organizationSwitcher.label', 'Organization')
361
+ }
362
+ return flatOrgOptions.find((opt) => opt.id === value)?.label.trim()
363
+ || t('organizationSwitcher.label', 'Organization')
364
+ }, [value, showAllOption, flatOrgOptions, t])
365
+
334
366
  if (state.status === 'hidden') {
335
367
  return null
336
368
  }
@@ -385,39 +417,6 @@ export default function OrganizationSwitcher({ compact }: OrganizationSwitcherEx
385
417
  )
386
418
  }
387
419
 
388
- const flatOrgOptions = React.useMemo(() => {
389
- const out: Array<{ id: string; label: string; selectable: boolean; depth: number }> = []
390
- const walk = (list: OrganizationTreeNode[]) => {
391
- for (const node of list) {
392
- const depth = typeof node.depth === 'number' ? node.depth : 0
393
- const indent = depth > 0 ? `${'  '.repeat(depth)}` : ''
394
- out.push({
395
- id: node.id,
396
- label: `${indent}${node.name}`,
397
- selectable: node.selectable !== false,
398
- depth,
399
- })
400
- if (Array.isArray(node.children) && node.children.length > 0) walk(node.children as OrganizationTreeNode[])
401
- }
402
- }
403
- walk(nodes)
404
- return out
405
- }, [nodes])
406
-
407
- const ALL_ORGS_SENTINEL = '__all__'
408
- const orgSelectValue = !value ? (showAllOption ? ALL_ORGS_SENTINEL : '') : value
409
- const [popoverOpen, setPopoverOpen] = React.useState(false)
410
-
411
- const activeOrgLabel = React.useMemo(() => {
412
- if (!value) {
413
- return showAllOption
414
- ? t('organizationSwitcher.allOrganizations', 'All organizations')
415
- : t('organizationSwitcher.label', 'Organization')
416
- }
417
- return flatOrgOptions.find((opt) => opt.id === value)?.label.trim()
418
- || t('organizationSwitcher.label', 'Organization')
419
- }, [value, showAllOption, flatOrgOptions, t])
420
-
421
420
  if (state.status === 'loading') {
422
421
  return <span className="hidden md:inline text-xs text-muted-foreground">{t('organizationSwitcher.loading')}</span>
423
422
  }
@@ -431,18 +430,20 @@ export default function OrganizationSwitcher({ compact }: OrganizationSwitcherEx
431
430
  return (
432
431
  <Popover open={popoverOpen} onOpenChange={setPopoverOpen}>
433
432
  <PopoverTrigger asChild>
434
- <button
433
+ <Button
435
434
  type="button"
435
+ variant="outline"
436
+ size="sm"
436
437
  aria-label={`${t('organizationSwitcher.label')}: ${activeOrgLabel}`}
437
438
  title={activeOrgLabel}
438
- className="inline-flex h-8 w-8 items-center justify-center gap-2 rounded-md border border-input bg-background px-0 text-sm font-medium shadow-xs transition-colors hover:bg-muted/40 focus:outline-none focus-visible:shadow-focus focus-visible:border-foreground data-[state=open]:bg-muted/40 sm:w-auto sm:justify-start sm:px-3 sm:max-w-[200px] md:max-w-[260px]"
439
+ className="w-8 px-0 hover:bg-muted/40 data-[state=open]:bg-muted/40 sm:w-auto sm:justify-start sm:px-3 sm:max-w-48 md:max-w-64"
439
440
  >
440
441
  <Building2 className="size-4 shrink-0 text-muted-foreground" aria-hidden="true" />
441
442
  <span className="hidden sm:block truncate flex-1 text-left">{activeOrgLabel}</span>
442
443
  <ChevronDown className="hidden sm:block size-4 shrink-0 text-muted-foreground" aria-hidden="true" />
443
- </button>
444
+ </Button>
444
445
  </PopoverTrigger>
445
- <PopoverContent align="end" className="w-[320px] p-0">
446
+ <PopoverContent align="end" className="w-80 p-0">
446
447
  {showTenantSelect ? (
447
448
  <div className="border-b p-3 space-y-2">
448
449
  <div className="text-overline font-medium uppercase tracking-wider text-muted-foreground/80 leading-none">
@@ -472,38 +473,43 @@ export default function OrganizationSwitcher({ compact }: OrganizationSwitcherEx
472
473
  <div className="px-2 py-1.5 text-overline font-medium uppercase tracking-wider text-muted-foreground/80 leading-none">
473
474
  {t('organizationSwitcher.label')}
474
475
  </div>
475
- <div className="max-h-[280px] overflow-y-auto">
476
+ <div className="max-h-72 overflow-y-auto">
476
477
  {showAllOption ? (
477
- <button
478
+ <Button
478
479
  type="button"
480
+ variant="ghost"
481
+ size="sm"
479
482
  onClick={() => {
480
483
  handleChange(null)
481
484
  setPopoverOpen(false)
482
485
  }}
483
- className="flex w-full items-center justify-between gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors hover:bg-muted/40 focus:outline-none focus-visible:bg-muted/40"
486
+ className="h-auto w-full justify-between rounded-sm px-2 py-1.5 text-left font-normal hover:bg-muted/40 focus-visible:bg-muted/40"
484
487
  >
485
488
  <span className="truncate min-w-0">{t('organizationSwitcher.allOrganizations', 'All organizations')}</span>
486
489
  {!value ? <Check className="size-4 shrink-0 text-accent-indigo" aria-hidden="true" /> : null}
487
- </button>
490
+ </Button>
488
491
  ) : null}
489
492
  {flatOrgOptions.map((opt) => {
490
493
  const isActive = value === opt.id
491
494
  return (
492
- <button
495
+ <Button
493
496
  key={opt.id}
494
497
  type="button"
498
+ variant="ghost"
499
+ size="sm"
495
500
  disabled={!opt.selectable}
496
501
  onClick={() => {
497
502
  if (!opt.selectable) return
498
503
  handleChange(opt.id)
499
504
  setPopoverOpen(false)
500
505
  }}
506
+ // DS-SKIP: dynamic tree indentation depends on organization depth.
501
507
  style={{ paddingLeft: `${0.5 + opt.depth * 0.75}rem` }}
502
- className="flex w-full items-center justify-between gap-2 rounded-sm py-1.5 pr-2 text-left text-sm transition-colors hover:bg-muted/40 disabled:opacity-50 disabled:hover:bg-transparent focus:outline-none focus-visible:bg-muted/40"
508
+ className="h-auto w-full justify-between rounded-sm py-1.5 pr-2 text-left font-normal hover:bg-muted/40 disabled:bg-transparent disabled:text-muted-foreground disabled:shadow-none focus-visible:bg-muted/40"
503
509
  >
504
510
  <span className="truncate min-w-0">{opt.label.trim()}</span>
505
511
  {isActive ? <Check className="size-4 shrink-0 text-accent-indigo" aria-hidden="true" /> : null}
506
- </button>
512
+ </Button>
507
513
  )
508
514
  })}
509
515
  </div>
@@ -59,7 +59,7 @@ function RoleTile({
59
59
 
60
60
  {disabled ? (
61
61
  <>
62
- <Button variant="outline" className="w-full cursor-not-allowed opacity-80" disabled>
62
+ <Button type="button" variant="outline" className="w-full cursor-not-allowed opacity-80" disabled>
63
63
  {disabledCtaLabel ?? defaultDisabledCtaLabel}
64
64
  </Button>
65
65
  {disabledMessage ? (
@@ -106,26 +106,26 @@ export function StartPageContent({ showStartPage: initialShowStartPage, showOnbo
106
106
  </section>
107
107
 
108
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">
109
+ <section className="rounded-lg border border-status-success-border bg-status-success-bg p-6 md:p-8 flex flex-col md:flex-row md:items-center gap-6 shadow-sm">
110
110
  <div className="flex items-start gap-4">
111
- <div className="rounded-full bg-emerald-600 text-white p-3">
111
+ <div className="rounded-full bg-status-success-icon text-status-success-bg p-3">
112
112
  <Rocket className="size-6" />
113
113
  </div>
114
114
  <div className="space-y-3">
115
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">
116
+ <h3 className="text-lg font-semibold text-status-success-text">{t('startPage.onboarding.title', 'Launch your own workspace')}</h3>
117
+ <p className="text-sm text-status-success-text/90">
118
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
119
  </p>
120
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">
121
+ <ul className="text-sm text-status-success-text/90 space-y-1 list-disc pl-5 marker:text-status-success-icon">
122
122
  <li>{t('startPage.onboarding.feature1', 'Automatic tenant and sample data provisioning')}</li>
123
123
  <li>{t('startPage.onboarding.feature2', 'Ready-to-use superadmin credentials after verification')}</li>
124
124
  </ul>
125
125
  </div>
126
126
  </div>
127
127
  <div className="md:ml-auto">
128
- <Button asChild className="bg-emerald-600 hover:bg-emerald-700 focus-visible:ring-ring px-6 py-5 text-base font-semibold text-white shadow-md">
128
+ <Button asChild size="lg" className="font-semibold shadow-md">
129
129
  <Link href="/onboarding">
130
130
  {t('startPage.onboarding.cta', 'Start onboarding')}
131
131
  <ArrowRight className="size-4" aria-hidden />
@@ -135,16 +135,16 @@ export function StartPageContent({ showStartPage: initialShowStartPage, showOnbo
135
135
  </section>
136
136
  ) : null}
137
137
 
138
- <section className="rounded-lg border bg-blue-50 dark:bg-blue-950/20 border-blue-200 dark:border-blue-900 p-4">
138
+ <section className="rounded-lg border border-status-info-border bg-status-info-bg p-4">
139
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" />
140
+ <Info className="size-5 text-status-info-icon shrink-0 mt-0.5" />
141
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">
142
+ <h3 className="text-sm font-semibold text-status-info-text mb-1">{t('startPage.defaultPassword.title', 'Default Password')}</h3>
143
+ <p className="text-sm text-status-info-text">
144
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>.
145
+ <code className="px-1.5 py-0.5 rounded border border-status-info-border/70 bg-background/70 font-mono text-xs text-foreground">secret</code>.
146
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>
147
+ <code className="px-1.5 py-0.5 rounded border border-status-info-border/70 bg-background/70 font-mono text-xs text-foreground">yarn mercato auth set-password --email &lt;email&gt; --password &lt;newPassword&gt;</code>
148
148
  <span className="mt-2 block">{t('startPage.defaultPassword.description3', 'Demo account emails are printed in the terminal output during yarn initialize.')}</span>
149
149
  </p>
150
150
  </div>
@@ -530,6 +530,7 @@
530
530
  "notices.cookies.dismiss": "Schließen",
531
531
  "notices.demo.and": "und",
532
532
  "notices.demo.description": "Diese Instanz wird nur zu Demonstrationszwecken bereitgestellt. Daten können jederzeit zurückgesetzt werden und werden nicht für einen garantierten Zeitraum aufbewahrt.",
533
+ "notices.demo.dismiss": "Demo-Hinweis schließen",
533
534
  "notices.demo.installLink": "Open Mercato lokal installieren",
534
535
  "notices.demo.reviewLinks": "Lesen Sie unsere",
535
536
  "notices.demo.superadminQuestion": "Benötigen Sie dauerhaften Superadmin-Zugriff?",
@@ -530,6 +530,7 @@
530
530
  "notices.cookies.dismiss": "Dismiss",
531
531
  "notices.demo.and": "and",
532
532
  "notices.demo.description": "This instance is provided for demo purposes only. Data may be reset at any time and is not retained for any guaranteed period.",
533
+ "notices.demo.dismiss": "Dismiss demo notice",
533
534
  "notices.demo.installLink": "Install Open Mercato locally",
534
535
  "notices.demo.reviewLinks": "Review our",
535
536
  "notices.demo.superadminQuestion": "Need persistent superadmin access?",
@@ -530,6 +530,7 @@
530
530
  "notices.cookies.dismiss": "Descartar",
531
531
  "notices.demo.and": "y",
532
532
  "notices.demo.description": "Esta instancia se proporciona solo con fines de demostración. Los datos pueden restablecerse en cualquier momento y no se conservan durante ningún período garantizado.",
533
+ "notices.demo.dismiss": "Descartar aviso de demostración",
533
534
  "notices.demo.installLink": "Instalar Open Mercato localmente",
534
535
  "notices.demo.reviewLinks": "Revise nuestros",
535
536
  "notices.demo.superadminQuestion": "¿Necesita acceso persistente de superadministrador?",
@@ -530,6 +530,7 @@
530
530
  "notices.cookies.dismiss": "Odrzuć",
531
531
  "notices.demo.and": "i",
532
532
  "notices.demo.description": "Ta instancja jest udostępniana wyłącznie do celów demonstracyjnych. Dane mogą zostać zresetowane w dowolnym momencie i nie są przechowywane przez żaden gwarantowany okres.",
533
+ "notices.demo.dismiss": "Zamknij komunikat demonstracyjny",
533
534
  "notices.demo.installLink": "Zainstaluj Open Mercato lokalnie",
534
535
  "notices.demo.reviewLinks": "Przejrzyj nasze",
535
536
  "notices.demo.superadminQuestion": "Potrzebujesz trwałego dostępu superadministratora?",