@salifbiaye/create-boutique 0.1.5 → 0.2.0

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 (64) hide show
  1. package/cli/dist/commands.js +3 -3
  2. package/cli/dist/create.js +5 -5
  3. package/cli/dist/generate.js +68 -10
  4. package/cli/dist/project.js +2 -0
  5. package/cli/dist/themes.js +5 -1
  6. package/package.json +1 -1
  7. package/templates/backend/src/lib/emails.ts +8 -1
  8. package/templates/backend/src/scripts/campaign-import.ts +6 -4
  9. package/templates/backend/src/scripts/seed.ts +15 -3
  10. package/templates/storefront/locales/en.json +33 -1
  11. package/templates/storefront/locales/fr.json +33 -1
  12. package/templates/storefront/package-lock.json +7 -0
  13. package/templates/storefront/package.json +1 -0
  14. package/templates/storefront/src/app/(main)/legal/[page]/page.tsx +3 -9
  15. package/templates/storefront/src/app/(main)/page.tsx +25 -0
  16. package/templates/storefront/src/app/(main)/store/page.tsx +3 -1
  17. package/templates/storefront/src/app/layout.tsx +9 -2
  18. package/templates/storefront/src/lib/boutique/config.ts +1 -0
  19. package/templates/storefront/src/lib/boutique/i18n-client.tsx +10 -2
  20. package/templates/storefront/src/lib/boutique/schema.ts +2 -0
  21. package/templates/storefront/src/modules/account/components/account-nav/index.tsx +50 -177
  22. package/templates/storefront/src/modules/account/components/address-card/add-address.tsx +2 -2
  23. package/templates/storefront/src/modules/account/components/address-card/edit-address-modal.tsx +2 -2
  24. package/templates/storefront/src/modules/account/components/overview/index.tsx +98 -143
  25. package/templates/storefront/src/modules/account/components/profile-billing-address/index.tsx +2 -3
  26. package/templates/storefront/src/modules/account/components/profile-phone/index.tsx +2 -3
  27. package/templates/storefront/src/modules/account/components/register/index.tsx +2 -3
  28. package/templates/storefront/src/modules/account/templates/account-layout.tsx +20 -24
  29. package/templates/storefront/src/modules/checkout/components/billing_address/index.tsx +2 -2
  30. package/templates/storefront/src/modules/checkout/components/shipping-address/index.tsx +2 -2
  31. package/templates/storefront/src/modules/common/components/phone-input/index.tsx +110 -0
  32. package/templates/storefront/src/modules/store/templates/index.tsx +82 -17
  33. package/templates/storefront/src/modules/store/templates/paginated-products.tsx +5 -0
  34. package/templates/storefront/src/modules/ub/campaign-blocks.tsx +1 -1
  35. package/templates/storefront/src/modules/ub/contact-page.tsx +78 -0
  36. package/templates/storefront/src/modules/ub/footer.tsx +26 -5
  37. package/templates/storefront/src/modules/ub/mobile-menu.tsx +49 -0
  38. package/templates/storefront/src/modules/ub/nav.tsx +2 -0
  39. package/templates/storefront/src/modules/ub/showcase.tsx +137 -0
  40. package/templates/storefront/src/styles/ub/account.css +199 -0
  41. package/templates/storefront/src/styles/ub/black-friday.css +96 -0
  42. package/templates/storefront/src/styles/ub/forms.css +61 -0
  43. package/templates/storefront/src/styles/ub/pages.css +382 -0
  44. package/templates/storefront/src/styles/ub/responsive.css +57 -0
  45. package/templates/storefront/src/styles/ub/showcase.css +85 -0
  46. package/templates/storefront/src/styles/ub/variants.css +64 -0
  47. package/templates/storefront/themes/black-friday-2/campaign.en.json +31 -0
  48. package/templates/storefront/themes/black-friday-2/campaign.fr.json +31 -0
  49. package/templates/storefront/themes/black-friday-2/theme.json +10 -0
  50. package/templates/storefront/themes/cyber-monday-2/campaign.en.json +31 -0
  51. package/templates/storefront/themes/cyber-monday-2/campaign.fr.json +31 -0
  52. package/templates/storefront/themes/cyber-monday-2/theme.json +10 -0
  53. package/templates/storefront/themes/halloween-2/campaign.en.json +31 -0
  54. package/templates/storefront/themes/halloween-2/campaign.fr.json +31 -0
  55. package/templates/storefront/themes/halloween-2/theme.json +10 -0
  56. package/templates/storefront/themes/noel-2/campaign.en.json +31 -0
  57. package/templates/storefront/themes/noel-2/campaign.fr.json +31 -0
  58. package/templates/storefront/themes/noel-2/theme.json +10 -0
  59. package/templates/storefront/themes/nouvel-an-2/campaign.en.json +31 -0
  60. package/templates/storefront/themes/nouvel-an-2/campaign.fr.json +31 -0
  61. package/templates/storefront/themes/nouvel-an-2/theme.json +10 -0
  62. package/templates/storefront/themes/thanksgiving-2/campaign.en.json +31 -0
  63. package/templates/storefront/themes/thanksgiving-2/campaign.fr.json +31 -0
  64. package/templates/storefront/themes/thanksgiving-2/theme.json +10 -0
@@ -68,6 +68,8 @@ export const themeSchema = z.object({
68
68
  id: z.string().regex(SLUG),
69
69
  name: z.string().min(1),
70
70
  reference: z.string().optional(),
71
+ /** Variante : reprend la mise en page (règles CSS) de ce thème, avec la palette de styles/ub/variants.css. */
72
+ layout: z.string().regex(SLUG).optional(),
71
73
  fonts: z.object({
72
74
  head: z.enum(["inter", "jakarta", "oswald", "outfit", "poppins", "serif", "sora", "space-grotesk"]),
73
75
  body: z.enum(["inter", "jakarta", "oswald", "outfit", "poppins", "serif", "sora", "space-grotesk"]),
@@ -1,197 +1,70 @@
1
1
  "use client"
2
2
 
3
-
4
3
  import { T } from "@lib/boutique/i18n-client"
5
- import { clx } from "@medusajs/ui"
6
4
  import { ArrowRightOnRectangle } from "@medusajs/icons"
7
5
  import { usePathname } from "next/navigation"
8
6
 
9
- import ChevronDown from "@modules/common/icons/chevron-down"
10
- import User from "@modules/common/icons/user"
7
+ import LocalizedClientLink from "@modules/common/components/localized-client-link"
11
8
  import MapPin from "@modules/common/icons/map-pin"
12
9
  import Package from "@modules/common/icons/package"
13
- import LocalizedClientLink from "@modules/common/components/localized-client-link"
10
+ import User from "@modules/common/icons/user"
11
+ import { Icon } from "@modules/ub/icons"
14
12
  import { HttpTypes } from "@medusajs/types"
15
13
  import { signout } from "@lib/data/customer"
16
14
 
17
- const AccountNav = ({
18
- customer,
19
- }: {
20
- customer: HttpTypes.StoreCustomer | null
21
- }) => {
22
- const route = usePathname()
15
+ const LINKS = [
16
+ { href: "/account", key: "account.overview", testId: "overview-link", icon: <Icon name="home" /> },
17
+ { href: "/account/profile", key: "account.profile", testId: "profile-link", icon: <User size={20} /> },
18
+ { href: "/account/addresses", key: "account.addresses", testId: "addresses-link", icon: <MapPin size={20} /> },
19
+ { href: "/account/orders", key: "account.orders", testId: "orders-link", icon: <Package size={20} /> },
20
+ ]
23
21
 
24
- const handleLogout = async () => {
25
- await signout()
26
- }
22
+ /** Menu du compte : carte client (initiales, nom, e-mail) + liens avec icônes ; pastilles défilantes sur mobile. */
23
+ const AccountNav = ({ customer }: { customer: HttpTypes.StoreCustomer | null }) => {
24
+ const route = usePathname()
25
+ const initials = `${customer?.first_name?.[0] ?? ""}${customer?.last_name?.[0] ?? ""}`.toUpperCase() || "•"
26
+ const name = [customer?.first_name, customer?.last_name].filter(Boolean).join(" ")
27
27
 
28
28
  return (
29
- <div>
30
- <div className="small:hidden" data-testid="mobile-account-nav">
31
- {route !== "/account" ? (
32
- <LocalizedClientLink
33
- href="/account"
34
- className="flex items-center gap-x-2 text-small-regular py-2"
35
- data-testid="account-main-link"
36
- >
37
- <>
38
- <ChevronDown className="transform rotate-90" />
39
- <span><T k="account.title" /></span>
40
- </>
41
- </LocalizedClientLink>
42
- ) : (
43
- <>
44
- <div className="text-xl-semi mb-4 px-8">
45
- Hello {customer?.first_name}
46
- </div>
47
- <div className="text-base-regular">
48
- <ul>
49
- <li>
50
- <LocalizedClientLink
51
- href="/account/profile"
52
- className="flex items-center justify-between py-4 border-b border-gray-200 px-8"
53
- data-testid="profile-link"
54
- >
55
- <>
56
- <div className="flex items-center gap-x-2">
57
- <User size={20} />
58
- <span><T k="account.profile" /></span>
59
- </div>
60
- <ChevronDown className="transform -rotate-90" />
61
- </>
62
- </LocalizedClientLink>
63
- </li>
64
- <li>
65
- <LocalizedClientLink
66
- href="/account/addresses"
67
- className="flex items-center justify-between py-4 border-b border-gray-200 px-8"
68
- data-testid="addresses-link"
69
- >
70
- <>
71
- <div className="flex items-center gap-x-2">
72
- <MapPin size={20} />
73
- <span><T k="account.addresses" /></span>
74
- </div>
75
- <ChevronDown className="transform -rotate-90" />
76
- </>
77
- </LocalizedClientLink>
78
- </li>
79
- <li>
80
- <LocalizedClientLink
81
- href="/account/orders"
82
- className="flex items-center justify-between py-4 border-b border-gray-200 px-8"
83
- data-testid="orders-link"
84
- >
85
- <div className="flex items-center gap-x-2">
86
- <Package size={20} />
87
- <span><T k="account.orders" /></span>
88
- </div>
89
- <ChevronDown className="transform -rotate-90" />
90
- </LocalizedClientLink>
91
- </li>
92
- <li>
93
- <button
94
- type="button"
95
- className="flex items-center justify-between py-4 border-b border-gray-200 px-8 w-full"
96
- onClick={handleLogout}
97
- data-testid="logout-button"
98
- >
99
- <div className="flex items-center gap-x-2">
100
- <ArrowRightOnRectangle />
101
- <span><T k="account.logout" /></span>
102
- </div>
103
- <ChevronDown className="transform -rotate-90" />
104
- </button>
105
- </li>
106
- </ul>
107
- </div>
108
- </>
109
- )}
29
+ <aside className="ub-accnav" data-testid="account-nav">
30
+ <div className="ub-accnav__me">
31
+ <span className="ub-accnav__avatar" aria-hidden>
32
+ {initials}
33
+ </span>
34
+ <span className="ub-accnav__who">
35
+ <b>{name || <T k="account.title" />}</b>
36
+ {customer?.email && <small>{customer.email}</small>}
37
+ </span>
110
38
  </div>
111
- <div className="hidden small:block" data-testid="account-nav">
112
- <div>
113
- <div className="pb-4">
114
- <h3 className="text-base-semi">Account</h3>
115
- </div>
116
- <div className="text-base-regular">
117
- <ul className="flex mb-0 justify-start items-start flex-col gap-y-4">
118
- <li>
119
- <AccountNavLink
120
- href="/account"
121
- route={route!}
122
- data-testid="overview-link"
123
- >
124
- <T k="account.overview" />
125
- </AccountNavLink>
126
- </li>
127
- <li>
128
- <AccountNavLink
129
- href="/account/profile"
130
- route={route!}
131
- data-testid="profile-link"
132
- >
133
- <T k="account.profile" />
134
- </AccountNavLink>
135
- </li>
136
- <li>
137
- <AccountNavLink
138
- href="/account/addresses"
139
- route={route!}
140
- data-testid="addresses-link"
39
+ <nav aria-label="account">
40
+ <ul className="ub-accnav__list">
41
+ {LINKS.map((l) => {
42
+ const active = l.href === "/account" ? route === l.href : route?.startsWith(l.href)
43
+ return (
44
+ <li key={l.href}>
45
+ <LocalizedClientLink
46
+ href={l.href}
47
+ className="ub-accnav__link"
48
+ aria-current={active ? "page" : undefined}
49
+ data-testid={l.testId}
141
50
  >
142
- <T k="account.addresses" />
143
- </AccountNavLink>
51
+ <span className="ub-accnav__ic">{l.icon}</span>
52
+ <T k={l.key} />
53
+ </LocalizedClientLink>
144
54
  </li>
145
- <li>
146
- <AccountNavLink
147
- href="/account/orders"
148
- route={route!}
149
- data-testid="orders-link"
150
- >
151
- <T k="account.orders" />
152
- </AccountNavLink>
153
- </li>
154
- <li className="text-grey-700">
155
- <button
156
- type="button"
157
- onClick={handleLogout}
158
- data-testid="logout-button"
159
- >
160
- <T k="account.logout" />
161
- </button>
162
- </li>
163
- </ul>
164
- </div>
165
- </div>
166
- </div>
167
- </div>
168
- )
169
- }
170
-
171
- type AccountNavLinkProps = {
172
- href: string
173
- route: string
174
- children: React.ReactNode
175
- "data-testid"?: string
176
- }
177
-
178
- const AccountNavLink = ({
179
- href,
180
- route,
181
- children,
182
- "data-testid": dataTestId,
183
- }: AccountNavLinkProps) => {
184
- const active = route === href
185
- return (
186
- <LocalizedClientLink
187
- href={href}
188
- className={clx("text-ui-fg-subtle hover:text-ui-fg-base", {
189
- "text-ui-fg-base font-semibold": active,
190
- })}
191
- data-testid={dataTestId}
192
- >
193
- {children}
194
- </LocalizedClientLink>
55
+ )
56
+ })}
57
+ <li>
58
+ <button type="button" className="ub-accnav__link ub-accnav__logout" onClick={() => signout()} data-testid="logout-button">
59
+ <span className="ub-accnav__ic">
60
+ <ArrowRightOnRectangle />
61
+ </span>
62
+ <T k="account.logout" />
63
+ </button>
64
+ </li>
65
+ </ul>
66
+ </nav>
67
+ </aside>
195
68
  )
196
69
  }
197
70
 
@@ -9,6 +9,7 @@ import { useEffect, useState, useActionState } from "react"
9
9
  import useToggleState from "@lib/hooks/use-toggle-state"
10
10
  import CountrySelect from "@modules/checkout/components/country-select"
11
11
  import Input from "@modules/common/components/input"
12
+ import PhoneInput from "@modules/common/components/phone-input"
12
13
  import Modal from "@modules/common/components/modal"
13
14
  import { SubmitButton } from "@modules/checkout/components/submit-button"
14
15
  import { HttpTypes } from "@medusajs/types"
@@ -130,10 +131,9 @@ const AddAddress = ({
130
131
  autoComplete="country"
131
132
  data-testid="country-select"
132
133
  />
133
- <Input
134
+ <PhoneInput
134
135
  label="form.phone"
135
136
  name="phone"
136
- autoComplete="phone"
137
137
  data-testid="phone-input"
138
138
  />
139
139
  </div>
@@ -9,6 +9,7 @@ import { Button, Heading, Text, clx } from "@medusajs/ui"
9
9
  import useToggleState from "@lib/hooks/use-toggle-state"
10
10
  import CountrySelect from "@modules/checkout/components/country-select"
11
11
  import Input from "@modules/common/components/input"
12
+ import PhoneInput from "@modules/common/components/phone-input"
12
13
  import Modal from "@modules/common/components/modal"
13
14
  import Spinner from "@modules/common/icons/spinner"
14
15
  import { SubmitButton } from "@modules/checkout/components/submit-button"
@@ -204,10 +205,9 @@ const EditAddress: React.FC<EditAddressProps> = ({
204
205
  defaultValue={address.country_code || undefined}
205
206
  data-testid="country-select"
206
207
  />
207
- <Input
208
+ <PhoneInput
208
209
  label="form.phone"
209
210
  name="phone"
210
- autoComplete="phone"
211
211
  defaultValue={address.phone || undefined}
212
212
  data-testid="phone-input"
213
213
  />
@@ -1,168 +1,123 @@
1
- import { T } from "@lib/boutique/i18n-client"
2
- import { Container } from "@medusajs/ui"
1
+ import Link from "next/link"
3
2
 
4
- import ChevronDown from "@modules/common/icons/chevron-down"
5
- import LocalizedClientLink from "@modules/common/components/localized-client-link"
3
+ import { getBoutique } from "@lib/boutique/config"
4
+ import { t } from "@lib/boutique/i18n"
6
5
  import { convertToLocale } from "@lib/util/money"
7
6
  import { HttpTypes } from "@medusajs/types"
7
+ import { Icon } from "@modules/ub/icons"
8
8
 
9
9
  type OverviewProps = {
10
10
  customer: HttpTypes.StoreCustomer | null
11
11
  orders: HttpTypes.StoreOrder[] | null
12
12
  }
13
13
 
14
+ /** Tableau de bord du compte : carte d'accueil, 3 indicateurs cliquables, commandes récentes. Composant serveur. */
14
15
  const Overview = ({ customer, orders }: OverviewProps) => {
16
+ const { locale } = getBoutique()
17
+ const date = new Intl.DateTimeFormat(locale, { day: "numeric", month: "short", year: "numeric" })
18
+ const completion = getProfileCompletion(customer)
19
+ const stats = [
20
+ { href: "/account/orders", icon: "cart" as const, value: String(orders?.length ?? 0), label: t("account.orders"), testId: "orders-count" },
21
+ { href: "/account/profile", icon: "user" as const, value: `${completion}%`, label: t("account.profileDone"), testId: "customer-profile-completion", data: completion },
22
+ { href: "/account/addresses", icon: "home" as const, value: String(customer?.addresses?.length || 0), label: t("account.addresses"), testId: "addresses-count", data: customer?.addresses?.length || 0 },
23
+ ]
24
+
15
25
  return (
16
- <div data-testid="overview-page-wrapper">
17
- <div className="hidden small:block">
18
- <div className="text-xl-semi flex justify-between items-center mb-4">
19
- <span data-testid="welcome-message" data-value={customer?.first_name}>
20
- <T k="account.hello" vars={{ name: customer?.first_name ?? "" }} />
21
- </span>
22
- <span className="text-small-regular text-ui-fg-base">
23
- <T k="account.signedInAs" />{" "}
24
- <span
25
- className="font-semibold"
26
- data-testid="customer-email"
27
- data-value={customer?.email}
28
- >
26
+ <div className="ub-acc" data-testid="overview-page-wrapper">
27
+ <section className="ub-acc__hello">
28
+ <div>
29
+ <h1 className="ub-head" data-testid="welcome-message" data-value={customer?.first_name}>
30
+ {t("account.hello", { name: customer?.first_name ?? "" })}
31
+ </h1>
32
+ <p>
33
+ {t("account.signedInAs")}{" "}
34
+ <b data-testid="customer-email" data-value={customer?.email}>
29
35
  {customer?.email}
30
- </span>
31
- </span>
36
+ </b>
37
+ </p>
32
38
  </div>
33
- <div className="flex flex-col py-8 border-t border-gray-200">
34
- <div className="flex flex-col gap-y-4 h-full col-span-1 row-span-2 flex-1">
35
- <div className="flex items-start gap-x-16 mb-6">
36
- <div className="flex flex-col gap-y-4">
37
- <h3 className="text-large-semi"><T k="account.profile" /></h3>
38
- <div className="flex items-end gap-x-2">
39
- <span
40
- className="text-3xl-semi leading-none"
41
- data-testid="customer-profile-completion"
42
- data-value={getProfileCompletion(customer)}
43
- >
44
- {getProfileCompletion(customer)}%
45
- </span>
46
- <span className="uppercase text-base-regular text-ui-fg-subtle">
47
- <T k="account.completed" />
48
- </span>
49
- </div>
50
- </div>
39
+ <Link href="/store" className="ub-acc__shop">
40
+ {t("account.keepShopping")} <Icon name="arrowRight" />
41
+ </Link>
42
+ </section>
51
43
 
52
- <div className="flex flex-col gap-y-4">
53
- <h3 className="text-large-semi"><T k="account.addresses" /></h3>
54
- <div className="flex items-end gap-x-2">
55
- <span
56
- className="text-3xl-semi leading-none"
57
- data-testid="addresses-count"
58
- data-value={customer?.addresses?.length || 0}
59
- >
60
- {customer?.addresses?.length || 0}
61
- </span>
62
- <span className="uppercase text-base-regular text-ui-fg-subtle">
63
- <T k="account.saved" />
64
- </span>
65
- </div>
66
- </div>
67
- </div>
44
+ <ul className="ub-acc__stats">
45
+ {stats.map((s) => (
46
+ <li key={s.href}>
47
+ <Link href={s.href} className="ub-acc__stat">
48
+ <span className="ub-acc__ic">
49
+ <Icon name={s.icon} />
50
+ </span>
51
+ <b data-testid={s.testId} data-value={s.data ?? s.value}>
52
+ {s.value}
53
+ </b>
54
+ <small>{s.label}</small>
55
+ </Link>
56
+ </li>
57
+ ))}
58
+ </ul>
68
59
 
69
- <div className="flex flex-col gap-y-4">
70
- <div className="flex items-center gap-x-2">
71
- <h3 className="text-large-semi"><T k="account.recentOrders" /></h3>
72
- </div>
73
- <ul
74
- className="flex flex-col gap-y-4"
75
- data-testid="orders-wrapper"
76
- >
77
- {orders && orders.length > 0 ? (
78
- orders.slice(0, 5).map((order) => {
79
- return (
80
- <li
81
- key={order.id}
82
- data-testid="order-wrapper"
83
- data-value={order.id}
84
- >
85
- <LocalizedClientLink
86
- href={`/account/orders/details/${order.id}`}
87
- >
88
- <Container className="bg-gray-50 flex justify-between items-center p-4">
89
- <div className="grid grid-cols-3 grid-rows-2 text-small-regular gap-x-4 flex-1">
90
- <span className="font-semibold">Date placed</span>
91
- <span className="font-semibold">
92
- <T k="account.orderNumber" />
93
- </span>
94
- <span className="font-semibold">
95
- <T k="account.totalAmount" />
96
- </span>
97
- <span data-testid="order-created-date">
98
- {new Date(order.created_at).toDateString()}
99
- </span>
100
- <span
101
- data-testid="order-id"
102
- data-value={order.display_id}
103
- >
104
- #{order.display_id}
105
- </span>
106
- <span data-testid="order-amount">
107
- {convertToLocale({
108
- amount: order.total,
109
- currency_code: order.currency_code,
110
- })}
111
- </span>
112
- </div>
113
- <button
114
- className="flex items-center justify-between"
115
- data-testid="open-order-button"
116
- >
117
- <span className="sr-only">
118
- Go to order #{order.display_id}
119
- </span>
120
- <ChevronDown className="-rotate-90" />
121
- </button>
122
- </Container>
123
- </LocalizedClientLink>
124
- </li>
125
- )
126
- })
127
- ) : (
128
- <span data-testid="no-orders-message"><T k="account.noRecentOrders" /></span>
129
- )}
130
- </ul>
131
- </div>
132
- </div>
60
+ {completion < 100 && (
61
+ <div className="ub-acc__progress">
62
+ <span>{t("account.completeProfile")}</span>
63
+ <i style={{ ["--p" as string]: `${completion}%` }} />
133
64
  </div>
134
- </div>
65
+ )}
66
+
67
+ <section>
68
+ <div className="ub-acc__head">
69
+ <h2 className="ub-head">{t("account.recentOrders")}</h2>
70
+ {!!orders?.length && (
71
+ <Link href="/account/orders" className="ub-acc__more">
72
+ {t("section.viewAll")} <Icon name="arrowRight" />
73
+ </Link>
74
+ )}
75
+ </div>
76
+ <ul className="ub-acc__orders" data-testid="orders-wrapper">
77
+ {orders && orders.length > 0 ? (
78
+ orders.slice(0, 5).map((order) => (
79
+ <li key={order.id} data-testid="order-wrapper" data-value={order.id}>
80
+ <Link href={`/account/orders/details/${order.id}`} className="ub-acc__order" data-testid="open-order-button">
81
+ <span className="ub-acc__ic">
82
+ <Icon name="truck" />
83
+ </span>
84
+ <span>
85
+ <b data-testid="order-id" data-value={order.display_id}>
86
+ #{order.display_id}
87
+ </b>
88
+ <small data-testid="order-created-date">{date.format(new Date(order.created_at))}</small>
89
+ </span>
90
+ <span className="ub-acc__amount" data-testid="order-amount">
91
+ {convertToLocale({ amount: order.total, currency_code: order.currency_code })}
92
+ </span>
93
+ <Icon name="arrowRight" />
94
+ </Link>
95
+ </li>
96
+ ))
97
+ ) : (
98
+ <li className="ub-acc__empty" data-testid="no-orders-container">
99
+ <span className="ub-acc__ic">
100
+ <Icon name="cart" />
101
+ </span>
102
+ <b>{t("account.noRecentOrders")}</b>
103
+ <Link href="/store" className="ub-acc__shop">
104
+ {t("account.keepShopping")} <Icon name="arrowRight" />
105
+ </Link>
106
+ </li>
107
+ )}
108
+ </ul>
109
+ </section>
135
110
  </div>
136
111
  )
137
112
  }
138
113
 
139
114
  const getProfileCompletion = (customer: HttpTypes.StoreCustomer | null) => {
115
+ if (!customer) return 0
140
116
  let count = 0
141
-
142
- if (!customer) {
143
- return 0
144
- }
145
-
146
- if (customer.email) {
147
- count++
148
- }
149
-
150
- if (customer.first_name && customer.last_name) {
151
- count++
152
- }
153
-
154
- if (customer.phone) {
155
- count++
156
- }
157
-
158
- const billingAddress = customer.addresses?.find(
159
- (addr) => addr.is_default_billing
160
- )
161
-
162
- if (billingAddress) {
163
- count++
164
- }
165
-
117
+ if (customer.email) count++
118
+ if (customer.first_name && customer.last_name) count++
119
+ if (customer.phone) count++
120
+ if (customer.addresses?.find((addr) => addr.is_default_billing)) count++
166
121
  return (count / 4) * 100
167
122
  }
168
123
 
@@ -4,6 +4,7 @@ import React, { useEffect, useMemo, useActionState } from "react"
4
4
 
5
5
  import { useT } from "@lib/boutique/i18n-client"
6
6
  import Input from "@modules/common/components/input"
7
+ import PhoneInput from "@modules/common/components/phone-input"
7
8
  import NativeSelect from "@modules/common/components/native-select"
8
9
 
9
10
  import AccountInfo from "../account-info"
@@ -125,11 +126,9 @@ const ProfileBillingAddress: React.FC<MyInformationProps> = ({
125
126
  defaultValue={billingAddress?.company || undefined}
126
127
  data-testid="billing-company-input"
127
128
  />
128
- <Input
129
+ <PhoneInput
129
130
  label="form.phone"
130
131
  name="phone"
131
- type="phone"
132
- autoComplete="phone"
133
132
  required
134
133
  defaultValue={billingAddress?.phone ?? customer?.phone ?? ""}
135
134
  data-testid="billing-phone-input"
@@ -3,6 +3,7 @@
3
3
  import React, { useEffect, useActionState } from "react";
4
4
 
5
5
  import Input from "@modules/common/components/input"
6
+ import PhoneInput from "@modules/common/components/phone-input"
6
7
 
7
8
  import AccountInfo from "../account-info"
8
9
  import { HttpTypes } from "@medusajs/types"
@@ -56,11 +57,9 @@ const ProfileEmail: React.FC<MyInformationProps> = ({ customer }) => {
56
57
  data-testid="account-phone-editor"
57
58
  >
58
59
  <div className="grid grid-cols-1 gap-y-2">
59
- <Input
60
+ <PhoneInput
60
61
  label="form.phone"
61
62
  name="phone"
62
- type="phone"
63
- autoComplete="phone"
64
63
  required
65
64
  defaultValue={customer.phone ?? ""}
66
65
  data-testid="phone-input"
@@ -4,6 +4,7 @@
4
4
  import { T } from "@lib/boutique/i18n-client"
5
5
  import { useActionState } from "react"
6
6
  import Input from "@modules/common/components/input"
7
+ import PhoneInput from "@modules/common/components/phone-input"
7
8
  import { LOGIN_VIEW } from "@modules/account/templates/login-template"
8
9
  import ErrorMessage from "@modules/checkout/components/error-message"
9
10
  import { SubmitButton } from "@modules/checkout/components/submit-button"
@@ -52,11 +53,9 @@ const Register = ({ setCurrentView }: Props) => {
52
53
  autoComplete="email"
53
54
  data-testid="email-input"
54
55
  />
55
- <Input
56
+ <PhoneInput
56
57
  label="form.phone"
57
58
  name="phone"
58
- type="tel"
59
- autoComplete="tel"
60
59
  data-testid="phone-input"
61
60
  />
62
61
  <Input