negodesign 0.0.29 → 0.0.31

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 (131) hide show
  1. package/dist/assets/lonely-404.mp4 +0 -0
  2. package/dist/components/core/banner/AdCardBanner.svelte.d.ts +4 -0
  3. package/dist/components/core/banner/CtaCardBanner.svelte +118 -0
  4. package/dist/components/core/banner/CtaCardBanner.svelte.d.ts +4 -0
  5. package/dist/components/core/banner/{BannerNotification.svelte → NotificationBanner.svelte} +4 -3
  6. package/dist/components/core/banner/NotificationBanner.svelte.d.ts +4 -0
  7. package/dist/components/core/banner/types.d.ts +38 -7
  8. package/dist/components/core/card/highlight/01/CardHighlight01.svelte +15 -26
  9. package/dist/components/core/card/highlight/02/CardHighlight02.svelte +13 -24
  10. package/dist/components/core/card/media/01/CardMedia01.svelte +2 -12
  11. package/dist/components/core/card/media/02/CardMedia02.svelte +2 -12
  12. package/dist/components/core/card/promotion/CardPromotion.svelte +5 -1
  13. package/dist/components/core/card/shared/CardButton.svelte +9 -3
  14. package/dist/components/core/card/shared/CardButton.svelte.d.ts +1 -1
  15. package/dist/components/core/card/shared/CardLogo.svelte +19 -0
  16. package/dist/components/core/card/shared/CardLogo.svelte.d.ts +7 -0
  17. package/dist/components/core/card/shared/CardThumbnailVideo.svelte +3 -0
  18. package/dist/components/core/card/types.d.ts +97 -0
  19. package/dist/components/core/carousel/grid/media/types.d.ts +11 -0
  20. package/dist/components/core/carousel/grid/media/ui/CarouselGridMedia.svelte +2 -1
  21. package/dist/components/core/carousel/grid/profile/types.d.ts +11 -0
  22. package/dist/components/core/carousel/grid/profile/ui/CarouselGridProfile.svelte +3 -5
  23. package/dist/components/core/carousel/grid/shared/ui/CarouselGridSlot.svelte +4 -9
  24. package/dist/components/core/carousel/grid/shared/ui/CarouselGridSlot.svelte.d.ts +1 -0
  25. package/dist/components/core/carousel/highlights/types.d.ts +11 -0
  26. package/dist/components/core/carousel/promotion/types.d.ts +10 -0
  27. package/dist/components/core/carousel/types.d.ts +56 -0
  28. package/dist/components/core/datatable/data/data-table.d.ts +25 -0
  29. package/dist/components/core/footer/types.d.ts +51 -0
  30. package/dist/components/core/footer/types.js +1 -0
  31. package/dist/components/core/footer/ui/01/Footer01.svelte +76 -0
  32. package/dist/components/core/footer/ui/01/Footer01.svelte.d.ts +4 -0
  33. package/dist/components/core/footer/ui/02/Footer02.svelte +53 -0
  34. package/dist/components/core/footer/ui/02/Footer02.svelte.d.ts +4 -0
  35. package/dist/components/core/footer/ui/Footer.svelte +18 -0
  36. package/dist/components/core/footer/ui/Footer.svelte.d.ts +7 -0
  37. package/dist/components/core/footer/ui/shared/FooterColumn.svelte +18 -0
  38. package/dist/components/core/footer/ui/shared/FooterColumn.svelte.d.ts +4 -0
  39. package/dist/components/core/footer/ui/shared/FooterNewsLetter.svelte +57 -0
  40. package/dist/components/core/footer/ui/shared/FooterNewsLetter.svelte.d.ts +9 -0
  41. package/dist/components/core/footer/ui/shared/FooterSocialIcon.svelte +62 -0
  42. package/dist/components/core/footer/ui/shared/FooterSocialIcon.svelte.d.ts +4 -0
  43. package/dist/components/core/nav/data/nav-menu-sidebar.d.ts +29 -0
  44. package/dist/components/core/nav/data/nav-menu.d.ts +43 -0
  45. package/dist/components/core/nav/data/nav-user-sidebar.d.ts +14 -0
  46. package/dist/components/core/nav/ui/menu-links.svelte +1 -1
  47. package/dist/components/core/panel/CarouselSlot.svelte +2 -2
  48. package/dist/components/core/panel/LeftHero.svelte +2 -2
  49. package/dist/components/core/panel/type.d.ts +11 -0
  50. package/dist/components/core/search/types.d.ts +17 -5
  51. package/dist/components/core/sidebar/types.d.ts +19 -0
  52. package/dist/components/core/sidebar/types.js +1 -0
  53. package/dist/components/core/tabs/data/TabModel.d.ts +11 -0
  54. package/dist/components/core/tabs/types.d.ts +11 -0
  55. package/dist/components/core/tabs/types.js +1 -0
  56. package/dist/components/pages/admin/01/ui/types.d.ts +21 -0
  57. package/dist/components/pages/admin/01/ui/types.js +1 -0
  58. package/dist/components/pages/product-details/01/ProductDetails01.svelte +71 -0
  59. package/dist/components/pages/product-details/01/ProductDetails01.svelte.d.ts +11 -0
  60. package/dist/components/pages/product-details/02/ProductDetails02.svelte +88 -0
  61. package/dist/components/pages/product-details/02/ProductDetails02.svelte.d.ts +11 -0
  62. package/dist/components/pages/product-details/03/ProductDetails03.svelte +77 -0
  63. package/dist/components/pages/product-details/03/ProductDetails03.svelte.d.ts +11 -0
  64. package/dist/components/pages/product-details/04/ProductDetails04.svelte +65 -0
  65. package/dist/components/pages/product-details/04/ProductDetails04.svelte.d.ts +11 -0
  66. package/dist/components/pages/product-details/ProductDetails.svelte +38 -0
  67. package/dist/components/pages/product-details/ProductDetails.svelte.d.ts +4 -0
  68. package/dist/components/pages/product-details/shared/ProductDetailActions.svelte +82 -0
  69. package/dist/components/pages/product-details/shared/ProductDetailActions.svelte.d.ts +13 -0
  70. package/dist/components/pages/product-details/shared/ProductDetailBreadcrumb.svelte +46 -0
  71. package/dist/components/pages/product-details/shared/ProductDetailBreadcrumb.svelte.d.ts +8 -0
  72. package/dist/components/pages/product-details/shared/ProductDetailDescription.svelte +38 -0
  73. package/dist/components/pages/product-details/shared/ProductDetailDescription.svelte.d.ts +8 -0
  74. package/dist/components/pages/product-details/shared/ProductDetailFeatures.svelte +40 -0
  75. package/dist/components/pages/product-details/shared/ProductDetailFeatures.svelte.d.ts +7 -0
  76. package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte +72 -0
  77. package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte.d.ts +9 -0
  78. package/dist/components/pages/product-details/shared/ProductDetailHeader.svelte +41 -0
  79. package/dist/components/pages/product-details/shared/ProductDetailHeader.svelte.d.ts +9 -0
  80. package/dist/components/pages/product-details/shared/ProductDetailImage.svelte +70 -0
  81. package/dist/components/pages/product-details/shared/ProductDetailImage.svelte.d.ts +9 -0
  82. package/dist/components/pages/product-details/shared/ProductDetailMap.svelte +57 -0
  83. package/dist/components/pages/product-details/shared/ProductDetailMap.svelte.d.ts +9 -0
  84. package/dist/components/pages/product-details/shared/ProductDetailPrice.svelte +44 -0
  85. package/dist/components/pages/product-details/shared/ProductDetailPrice.svelte.d.ts +9 -0
  86. package/dist/components/pages/product-details/shared/ProductDetailRating.svelte +42 -0
  87. package/dist/components/pages/product-details/shared/ProductDetailRating.svelte.d.ts +8 -0
  88. package/dist/components/pages/product-details/shared/ProductDetailReviews.svelte +70 -0
  89. package/dist/components/pages/product-details/shared/ProductDetailReviews.svelte.d.ts +8 -0
  90. package/dist/components/pages/product-details/shared/ProductDetailTabs.svelte +96 -0
  91. package/dist/components/pages/product-details/shared/ProductDetailTabs.svelte.d.ts +12 -0
  92. package/dist/components/pages/product-details/shared/ProductDetailTags.svelte +35 -0
  93. package/dist/components/pages/product-details/shared/ProductDetailTags.svelte.d.ts +8 -0
  94. package/dist/components/pages/product-details/shared/ProductPurchasePanel.svelte +58 -0
  95. package/dist/components/pages/product-details/shared/ProductPurchasePanel.svelte.d.ts +11 -0
  96. package/dist/components/pages/product-details/types.d.ts +170 -0
  97. package/dist/components/pages/product-details/types.js +1 -0
  98. package/dist/components/pages/profile-user/01/ProfileUser01.svelte +113 -0
  99. package/dist/components/pages/profile-user/01/ProfileUser01.svelte.d.ts +27 -0
  100. package/dist/components/pages/profile-user/02/ProfileUser02.svelte +83 -0
  101. package/dist/components/pages/profile-user/02/ProfileUser02.svelte.d.ts +20 -0
  102. package/dist/components/pages/profile-user/03/ProfileUser03.svelte +85 -0
  103. package/dist/components/pages/profile-user/03/ProfileUser03.svelte.d.ts +20 -0
  104. package/dist/components/pages/profile-user/ProfileUser.svelte +53 -0
  105. package/dist/components/pages/profile-user/ProfileUser.svelte.d.ts +4 -0
  106. package/dist/components/pages/profile-user/shared/ProfileUserAddress.svelte +84 -0
  107. package/dist/components/pages/profile-user/shared/ProfileUserAddress.svelte.d.ts +8 -0
  108. package/dist/components/pages/profile-user/shared/ProfileUserHeader.svelte +104 -0
  109. package/dist/components/pages/profile-user/shared/ProfileUserHeader.svelte.d.ts +9 -0
  110. package/dist/components/pages/profile-user/shared/ProfileUserResetPassword.svelte +116 -0
  111. package/dist/components/pages/profile-user/shared/ProfileUserResetPassword.svelte.d.ts +7 -0
  112. package/dist/components/pages/profile-user/shared/ProfileUserSetting.svelte +176 -0
  113. package/dist/components/pages/profile-user/shared/ProfileUserSetting.svelte.d.ts +14 -0
  114. package/dist/components/pages/profile-user/shared/ProfileUserTabs.svelte +78 -0
  115. package/dist/components/pages/profile-user/shared/ProfileUserTabs.svelte.d.ts +22 -0
  116. package/dist/components/pages/profile-user/types.d.ts +144 -0
  117. package/dist/components/pages/profile-user/types.js +1 -0
  118. package/dist/globals.css +319 -4
  119. package/dist/i18n/langs/en.d.ts +87 -0
  120. package/dist/i18n/langs/en.js +87 -0
  121. package/dist/i18n/langs/pt.d.ts +87 -0
  122. package/dist/i18n/langs/pt.js +93 -6
  123. package/dist/i18n/translations.d.ts +174 -0
  124. package/dist/index.d.ts +194 -27
  125. package/dist/index.js +194 -27
  126. package/dist/styles/globals.css +1 -1
  127. package/dist/types/index.d.ts +196 -5
  128. package/package.json +1 -1
  129. package/dist/components/core/banner/BannerNotification.svelte.d.ts +0 -4
  130. package/dist/components/core/banner/CardAdBanner.svelte.d.ts +0 -4
  131. /package/dist/components/core/banner/{CardAdBanner.svelte → AdCardBanner.svelte} +0 -0
@@ -0,0 +1,84 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Lista de moradas do utilizador com opção de adicionar e badge para default.
4
+ * Extraído da variante 1. Todos os textos são parametrizáveis com defaults via i18n.
5
+ * @component
6
+ */
7
+ </script>
8
+
9
+ <script lang="ts">
10
+ import { MapPin } from "@hugeicons/core-free-icons";
11
+ import { HugeiconsIcon } from "@hugeicons/svelte";
12
+ import PlusIcon from "@lucide/svelte/icons/plus";
13
+ import Badge from "../../../ui/badge/badge.svelte";
14
+ import Button from "../../../ui/button/button.svelte";
15
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
16
+ import { cn } from "../../../../utils.js";
17
+ import { t } from "../../../../i18n";
18
+ import type { ProfileUserAddress, ProfileUserAddressProps } from "../types";
19
+
20
+ let {
21
+ addresses,
22
+ isLoading = false,
23
+ onAddAddress,
24
+ className,
25
+ title = $t("profile-user.variant1.addresses.title"),
26
+ addLabel = $t("profile-user.variant1.addresses.add"),
27
+ defaultBadge = $t("profile-user.variant1.addresses.defaultBadge"),
28
+ emptyText = $t("profile-user.variant1.addresses.empty"),
29
+ }: ProfileUserAddressProps & {
30
+ addresses?: ProfileUserAddress[];
31
+ isLoading?: boolean;
32
+ } = $props();
33
+ </script>
34
+
35
+ <section
36
+ class={cn("rounded-lg border bg-card p-6", className)}
37
+ data-slot="profile-user-address"
38
+ >
39
+ <div class="mb-6 flex items-center justify-between">
40
+ <h3 class="text-lg font-semibold">{title}</h3>
41
+ <Button variant="ghost" size="sm" onclick={onAddAddress}>
42
+ <PlusIcon class="size-4" />
43
+ {addLabel}
44
+ </Button>
45
+ </div>
46
+
47
+ {#if isLoading}
48
+ <div class="grid gap-4 md:grid-cols-2">
49
+ {#each Array.from({ length: 2 }) as _, i (i)}
50
+ <Skeleton class="h-32 w-full rounded-md" />
51
+ {/each}
52
+ </div>
53
+ {:else if addresses && addresses.length > 0}
54
+ <div class="grid gap-4 md:grid-cols-2">
55
+ {#each addresses as address (address.id)}
56
+ <div
57
+ class={cn(
58
+ "rounded-md border p-4 transition-colors",
59
+ address.isDefault ? "border-primary bg-primary/5" : ""
60
+ )}
61
+ >
62
+ <div class="mb-2 flex items-center justify-between">
63
+ <span class="font-medium">{address.label}</span>
64
+ {#if address.isDefault}
65
+ <Badge>{defaultBadge}</Badge>
66
+ {/if}
67
+ </div>
68
+ <div class="flex flex-col gap-1 text-sm text-muted-foreground">
69
+ <p class="flex items-start gap-1.5">
70
+ <HugeiconsIcon icon={MapPin} class="mt-0.5 size-4 shrink-0" />
71
+ {address.street}
72
+ {#if address.apartment}, {address.apartment}{/if}
73
+ </p>
74
+ {#if address.state || address.zip}
75
+ <p class="pl-5.5">{address.state ?? ""} {address.zip ?? ""}</p>
76
+ {/if}
77
+ </div>
78
+ </div>
79
+ {/each}
80
+ </div>
81
+ {:else}
82
+ <p class="text-sm text-muted-foreground">{emptyText}</p>
83
+ {/if}
84
+ </section>
@@ -0,0 +1,8 @@
1
+ import type { ProfileUserAddress, ProfileUserAddressProps } from "../types";
2
+ type $$ComponentProps = ProfileUserAddressProps & {
3
+ addresses?: ProfileUserAddress[];
4
+ isLoading?: boolean;
5
+ };
6
+ declare const ProfileUserAddress: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type ProfileUserAddress = ReturnType<typeof ProfileUserAddress>;
8
+ export default ProfileUserAddress;
@@ -0,0 +1,104 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Header da página de perfil: gradiente amarelo/laranja, avatar à esquerda,
4
+ * nome + status, role, email/telefone e botão "LOGIN AS USER".
5
+ * @component
6
+ */
7
+ </script>
8
+
9
+ <script lang="ts">
10
+ import {
11
+ BadgeCheckIcon,
12
+ Mail01Icon,
13
+ UserIcon,
14
+ } from "@hugeicons/core-free-icons";
15
+ import { HugeiconsIcon } from "@hugeicons/svelte";
16
+ import Avatar from "../../../ui/avatar/avatar.svelte";
17
+ import AvatarImage from "../../../ui/avatar/avatar-image.svelte";
18
+ import Badge from "../../../ui/badge/badge.svelte";
19
+ import Button from "../../../ui/button/button.svelte";
20
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
21
+ import { t } from "../../../../i18n";
22
+ import type { ProfileUserData, ProfileUserHeaderProps } from "../types";
23
+
24
+ let {
25
+ data,
26
+ isLoading = false,
27
+ fullName,
28
+ onLoginAsUser,
29
+ statusDefault = $t("profile-user.variant1.statusActive"),
30
+ roleDefault = $t("profile-user.variant1.roleDefault"),
31
+ loginAsUserLabel = $t("profile-user.variant1.actionLoginAsUser"),
32
+ }: ProfileUserHeaderProps & {
33
+ data: ProfileUserData;
34
+ isLoading?: boolean;
35
+ fullName: string;
36
+ } = $props();
37
+ </script>
38
+
39
+ <header
40
+ class="bg-linear-to-r from-blue-400 to-blue-500 px-6 py-10 text-white"
41
+ data-slot="profile-user-header"
42
+ >
43
+ <div class="mx-auto max-w-7xl">
44
+ <div
45
+ class="flex flex-col gap-6 md:flex-row md:items-center md:justify-between"
46
+ >
47
+ <div class="flex items-center gap-5">
48
+ {#if isLoading}
49
+ <Skeleton class="size-24 rounded-full" />
50
+ {:else}
51
+ <Avatar size="lg" class="size-24 ring-4 ring-white/30">
52
+ <AvatarImage src={data.avatarUrl} alt={fullName} />
53
+ </Avatar>
54
+ {/if}
55
+ <div class="flex flex-col gap-2">
56
+ {#if isLoading}
57
+ <Skeleton class="h-8 w-48 rounded-md bg-white/30" />
58
+ <Skeleton class="h-5 w-36 rounded-md bg-white/20" />
59
+ <Skeleton class="h-4 w-44 rounded-md bg-white/20" />
60
+ {:else}
61
+ <div class="flex items-center gap-2">
62
+ <h1 class="text-2xl font-bold text-white">{fullName}</h1>
63
+ <Badge
64
+ variant="secondary"
65
+ class="bg-white/20 text-white hover:bg-white/30"
66
+ >
67
+ <HugeiconsIcon icon={BadgeCheckIcon} />
68
+ {data.status ?? statusDefault}
69
+ </Badge>
70
+ </div>
71
+ <p class="text-base font-medium text-white/90">
72
+ {data.role ?? roleDefault}
73
+ </p>
74
+ <div class="flex items-center gap-4 text-sm text-white/80">
75
+ <span class="flex items-center gap-1.5">
76
+ <HugeiconsIcon icon={Mail01Icon} class="size-4" />
77
+ {data.email}
78
+ </span>
79
+ {#if data.phone}
80
+ <span class="flex items-center gap-1.5">
81
+ <HugeiconsIcon icon={UserIcon} class="size-4" />
82
+ {data.phone}
83
+ </span>
84
+ {/if}
85
+ </div>
86
+ {/if}
87
+ </div>
88
+ </div>
89
+ <div>
90
+ {#if isLoading}
91
+ <Skeleton class="h-9 w-36 rounded-md bg-white/30" />
92
+ {:else}
93
+ <Button
94
+ variant="outline"
95
+ class="border-white/30 bg-white/10 text-white hover:bg-white/20 hover:text-white"
96
+ onclick={() => onLoginAsUser?.(data.id)}
97
+ >
98
+ {loginAsUserLabel}
99
+ </Button>
100
+ {/if}
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </header>
@@ -0,0 +1,9 @@
1
+ import type { ProfileUserData, ProfileUserHeaderProps } from "../types";
2
+ type $$ComponentProps = ProfileUserHeaderProps & {
3
+ data: ProfileUserData;
4
+ isLoading?: boolean;
5
+ fullName: string;
6
+ };
7
+ declare const ProfileUserHeader: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type ProfileUserHeader = ReturnType<typeof ProfileUserHeader>;
9
+ export default ProfileUserHeader;
@@ -0,0 +1,116 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Formulário de alteração de senha da variante 2 (aba "Security").
4
+ * Contém campos Current password, New password e Confirm new password,
5
+ * além dos botões Update Password e Cancel.
6
+ * @component
7
+ */
8
+ </script>
9
+
10
+ <script lang="ts">
11
+ import Button from "../../../ui/button/button.svelte";
12
+ import Input from "../../../ui/input/input.svelte";
13
+ import Label from "../../../ui/label/label.svelte";
14
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
15
+ import { cn } from "../../../../utils.js";
16
+ import { t } from "../../../../i18n";
17
+ import type { ProfileUserResetPasswordProps } from "../types";
18
+
19
+ let {
20
+ isLoading = false,
21
+ className,
22
+ onChangePassword,
23
+ onCancel,
24
+ title = $t("profile-user.resetPassword.title"),
25
+ currentLabel = $t("profile-user.resetPassword.current.label"),
26
+ currentPlaceholder = $t("profile-user.resetPassword.current.placeholder"),
27
+ newLabel = $t("profile-user.resetPassword.new.label"),
28
+ newPlaceholder = $t("profile-user.resetPassword.new.placeholder"),
29
+ confirmLabel = $t("profile-user.resetPassword.confirm.label"),
30
+ confirmPlaceholder = $t("profile-user.resetPassword.confirm.placeholder"),
31
+ submitLabel = $t("profile-user.resetPassword.submit"),
32
+ cancelLabel = $t("profile-user.resetPassword.cancel"),
33
+ }: ProfileUserResetPasswordProps & {
34
+ isLoading?: boolean;
35
+ } = $props();
36
+
37
+ let currentPassword = $state("");
38
+ let newPassword = $state("");
39
+ let confirmPassword = $state("");
40
+
41
+ const handleSubmit = () => {
42
+ onChangePassword?.({
43
+ currentPassword,
44
+ newPassword,
45
+ confirmPassword,
46
+ });
47
+ };
48
+
49
+ const handleCancel = () => {
50
+ currentPassword = "";
51
+ newPassword = "";
52
+ confirmPassword = "";
53
+ onCancel?.();
54
+ };
55
+ </script>
56
+
57
+ <section class={cn("rounded-lg border bg-card p-6", className)} data-slot="profile-user-reset-password">
58
+ <h3 class="mb-6 text-lg font-semibold">{title}</h3>
59
+
60
+ <form
61
+ class="flex max-w-md flex-col gap-5"
62
+ onsubmit={(e) => {
63
+ e.preventDefault();
64
+ handleSubmit();
65
+ }}
66
+ >
67
+ <div class="flex flex-col gap-2">
68
+ <Label for="currentPassword">{currentLabel}</Label>
69
+ {#if isLoading}
70
+ <Skeleton class="h-9 w-full rounded-md" />
71
+ {:else}
72
+ <Input
73
+ id="currentPassword"
74
+ type="password"
75
+ bind:value={currentPassword}
76
+ placeholder={currentPlaceholder}
77
+ />
78
+ {/if}
79
+ </div>
80
+
81
+ <div class="flex flex-col gap-2">
82
+ <Label for="newPassword">{newLabel}</Label>
83
+ {#if isLoading}
84
+ <Skeleton class="h-9 w-full rounded-md" />
85
+ {:else}
86
+ <Input
87
+ id="newPassword"
88
+ type="password"
89
+ bind:value={newPassword}
90
+ placeholder={newPlaceholder}
91
+ />
92
+ {/if}
93
+ </div>
94
+
95
+ <div class="flex flex-col gap-2">
96
+ <Label for="confirmPassword">{confirmLabel}</Label>
97
+ {#if isLoading}
98
+ <Skeleton class="h-9 w-full rounded-md" />
99
+ {:else}
100
+ <Input
101
+ id="confirmPassword"
102
+ type="password"
103
+ bind:value={confirmPassword}
104
+ placeholder={confirmPlaceholder}
105
+ />
106
+ {/if}
107
+ </div>
108
+
109
+ <div class="flex gap-3 pt-2">
110
+ <Button type="submit" disabled={isLoading}>{submitLabel}</Button>
111
+ <Button type="button" variant="outline" onclick={handleCancel} disabled={isLoading}>
112
+ {cancelLabel}
113
+ </Button>
114
+ </div>
115
+ </form>
116
+ </section>
@@ -0,0 +1,7 @@
1
+ import type { ProfileUserResetPasswordProps } from "../types";
2
+ type $$ComponentProps = ProfileUserResetPasswordProps & {
3
+ isLoading?: boolean;
4
+ };
5
+ declare const ProfileUserResetPassword: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type ProfileUserResetPassword = ReturnType<typeof ProfileUserResetPassword>;
7
+ export default ProfileUserResetPassword;
@@ -0,0 +1,176 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Preferências de notificações e marketing.
4
+ * Inclui secções "Email/Notification preferences" e "Enable notification",
5
+ * além das preferências de marketing.
6
+ * @component
7
+ */
8
+ </script>
9
+
10
+ <script lang="ts">
11
+ import Checkbox from "../../../ui/checkbox/checkbox.svelte";
12
+ import Label from "../../../ui/label/label.svelte";
13
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
14
+ import { cn } from "../../../../utils.js";
15
+ import { t } from "../../../../i18n";
16
+ import type {
17
+ ProfileUserMarketingPref,
18
+ ProfileUserNotificationPref,
19
+ ProfileUserSettingProps,
20
+ } from "../types";
21
+
22
+ let {
23
+ notificationPref,
24
+ enableNotification,
25
+ marketingPrefs,
26
+ isLoading = false,
27
+ className,
28
+ notificationTitle = $t("profile-user.setting.notification.title"),
29
+ notificationEmailLabel = $t("profile-user.setting.notification.email.label"),
30
+ notificationEmailDesc = $t("profile-user.setting.notification.email.desc"),
31
+ notificationWebLabel = $t("profile-user.setting.notification.web.label"),
32
+ notificationWebDesc = $t("profile-user.setting.notification.web.desc"),
33
+ notificationAppLabel = $t("profile-user.setting.notification.app.label"),
34
+ notificationAppDesc = $t("profile-user.setting.notification.app.desc"),
35
+ enableTitle = $t("profile-user.setting.enable.title"),
36
+ enableEmailLabel = $t("profile-user.setting.enable.email"),
37
+ enableWebLabel = $t("profile-user.setting.enable.web"),
38
+ enableAppLabel = $t("profile-user.setting.enable.app"),
39
+ marketingTitle = $t("profile-user.setting.marketing.title"),
40
+ marketingTypeLabel = $t("profile-user.setting.marketing.type"),
41
+ marketingEmailLabel = $t("profile-user.setting.marketing.email"),
42
+ marketingPushLabel = $t("profile-user.setting.marketing.push"),
43
+ }: ProfileUserSettingProps & {
44
+ notificationPref?: ProfileUserNotificationPref;
45
+ enableNotification?: { email?: boolean; web?: boolean; app?: boolean };
46
+ marketingPrefs?: ProfileUserMarketingPref[];
47
+ isLoading?: boolean;
48
+ } = $props();
49
+
50
+ let emailEnabled = $state(false);
51
+ let webEnabled = $state(false);
52
+ let appEnabled = $state(false);
53
+
54
+ let enableEmailChecked = $state(false);
55
+ let enableWebChecked = $state(false);
56
+ let enableAppChecked = $state(false);
57
+
58
+ $effect(() => {
59
+ emailEnabled = notificationPref?.emailEnabled ?? false;
60
+ webEnabled = notificationPref?.webEnabled ?? false;
61
+ appEnabled = notificationPref?.appEnabled ?? false;
62
+ });
63
+
64
+ $effect(() => {
65
+ enableEmailChecked = enableNotification?.email ?? false;
66
+ enableWebChecked = enableNotification?.web ?? false;
67
+ enableAppChecked = enableNotification?.app ?? false;
68
+ });
69
+
70
+ const marketingState = $state<Record<string, { email: boolean; push: boolean }>>({});
71
+ $effect(() => {
72
+ marketingPrefs?.forEach((pref) => {
73
+ if (!marketingState[pref.id]) {
74
+ marketingState[pref.id] = {
75
+ email: pref.email ?? false,
76
+ push: pref.push ?? false,
77
+ };
78
+ }
79
+ });
80
+ });
81
+ </script>
82
+
83
+ <div class={cn("flex flex-col gap-6", className)} data-slot="profile-user-setting">
84
+ <section class="rounded-lg border bg-card p-6">
85
+ <h3 class="mb-6 text-lg font-semibold">{notificationTitle}</h3>
86
+ {#if isLoading}
87
+ <div class="flex flex-col gap-4">
88
+ {#each Array.from({ length: 3 }) as _, i (i)}
89
+ <Skeleton class="h-6 w-full rounded-md" />
90
+ {/each}
91
+ </div>
92
+ {:else}
93
+ <div class="flex flex-col gap-4">
94
+ <div class="flex items-center justify-between">
95
+ <div>
96
+ <Label class="cursor-pointer">{notificationEmailLabel}</Label>
97
+ <p class="text-sm text-muted-foreground">{notificationEmailDesc}</p>
98
+ </div>
99
+ <Checkbox bind:checked={emailEnabled} />
100
+ </div>
101
+ <div class="flex items-center justify-between">
102
+ <div>
103
+ <Label class="cursor-pointer">{notificationWebLabel}</Label>
104
+ <p class="text-sm text-muted-foreground">{notificationWebDesc}</p>
105
+ </div>
106
+ <Checkbox bind:checked={webEnabled} />
107
+ </div>
108
+ <div class="flex items-center justify-between">
109
+ <div>
110
+ <Label class="cursor-pointer">{notificationAppLabel}</Label>
111
+ <p class="text-sm text-muted-foreground">{notificationAppDesc}</p>
112
+ </div>
113
+ <Checkbox bind:checked={appEnabled} />
114
+ </div>
115
+ </div>
116
+ {/if}
117
+ </section>
118
+
119
+ <section class="rounded-lg border bg-card p-6">
120
+ <h3 class="mb-6 text-lg font-semibold">{enableTitle}</h3>
121
+ {#if isLoading}
122
+ <div class="flex flex-col gap-4">
123
+ {#each Array.from({ length: 3 }) as _, i (i)}
124
+ <Skeleton class="h-6 w-full rounded-md" />
125
+ {/each}
126
+ </div>
127
+ {:else}
128
+ <div class="grid gap-4 sm:grid-cols-3">
129
+ <div class="flex flex-col items-center gap-2 rounded-md border p-4 text-center">
130
+ <Label class="cursor-pointer text-base font-medium">{enableEmailLabel}</Label>
131
+ <Checkbox bind:checked={enableEmailChecked} />
132
+ </div>
133
+ <div class="flex flex-col items-center gap-2 rounded-md border p-4 text-center">
134
+ <Label class="cursor-pointer text-base font-medium">{enableWebLabel}</Label>
135
+ <Checkbox bind:checked={enableWebChecked} />
136
+ </div>
137
+ <div class="flex flex-col items-center gap-2 rounded-md border p-4 text-center">
138
+ <Label class="cursor-pointer text-base font-medium">{enableAppLabel}</Label>
139
+ <Checkbox bind:checked={enableAppChecked} />
140
+ </div>
141
+ </div>
142
+ {/if}
143
+ </section>
144
+
145
+ {#if marketingPrefs && marketingPrefs.length > 0}
146
+ <section class="rounded-lg border bg-card p-6">
147
+ <h3 class="mb-6 text-lg font-semibold">{marketingTitle}</h3>
148
+ {#if isLoading}
149
+ <div class="flex flex-col gap-4">
150
+ {#each marketingPrefs as _, i (i)}
151
+ <Skeleton class="h-12 w-full rounded-md" />
152
+ {/each}
153
+ </div>
154
+ {:else}
155
+ <div class="overflow-hidden rounded-md border">
156
+ <div class="grid grid-cols-[1fr_auto_auto] gap-4 border-b bg-muted/50 px-4 py-3 text-sm font-medium">
157
+ <span>{marketingTypeLabel}</span>
158
+ <span class="text-center">{marketingEmailLabel}</span>
159
+ <span class="text-center">{marketingPushLabel}</span>
160
+ </div>
161
+ {#each marketingPrefs as pref (pref.id)}
162
+ <div class="grid grid-cols-[1fr_auto_auto] items-center gap-4 border-b px-4 py-3 last:border-b-0">
163
+ <span class="font-medium">{pref.label}</span>
164
+ <div class="flex justify-center">
165
+ <Checkbox bind:checked={marketingState[pref.id].email} />
166
+ </div>
167
+ <div class="flex justify-center">
168
+ <Checkbox bind:checked={marketingState[pref.id].push} />
169
+ </div>
170
+ </div>
171
+ {/each}
172
+ </div>
173
+ {/if}
174
+ </section>
175
+ {/if}
176
+ </div>
@@ -0,0 +1,14 @@
1
+ import type { ProfileUserMarketingPref, ProfileUserNotificationPref, ProfileUserSettingProps } from "../types";
2
+ type $$ComponentProps = ProfileUserSettingProps & {
3
+ notificationPref?: ProfileUserNotificationPref;
4
+ enableNotification?: {
5
+ email?: boolean;
6
+ web?: boolean;
7
+ app?: boolean;
8
+ };
9
+ marketingPrefs?: ProfileUserMarketingPref[];
10
+ isLoading?: boolean;
11
+ };
12
+ declare const ProfileUserSetting: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type ProfileUserSetting = ReturnType<typeof ProfileUserSetting>;
14
+ export default ProfileUserSetting;
@@ -0,0 +1,78 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Barra de tabs reutilizável para as variantes de perfil.
4
+ * Suporta loading state, tabs customizadas, active tab controlável
5
+ * e estilos variantes (underline vs pill).
6
+ * @component
7
+ */
8
+ export type ProfileUserTabItem = { id: string; label: string };
9
+ export type ProfileUserTabsStyle = "underline" | "pill";
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
14
+ import { cn } from "../../../../utils.js";
15
+
16
+ let {
17
+ tabs,
18
+ activeTab = $bindable(),
19
+ style = "underline",
20
+ isLoading = false,
21
+ className,
22
+ onchange,
23
+ }: {
24
+ tabs: ProfileUserTabItem[];
25
+ activeTab?: string;
26
+ style?: ProfileUserTabsStyle;
27
+ isLoading?: boolean;
28
+ className?: string;
29
+ onchange?: (id: string) => void;
30
+ } = $props();
31
+
32
+ const currentTab = $derived(activeTab ?? tabs[0]?.id ?? "");
33
+
34
+ const handleSelect = (id: string) => {
35
+ activeTab = id;
36
+ onchange?.(id);
37
+ };
38
+ </script>
39
+
40
+ <div
41
+ class={cn(
42
+ "mb-6 flex gap-1 border-b overflow-x-auto",
43
+ style === "pill" && "-mt-12 mb-6 rounded-t-lg border border-t bg-background px-2 py-1 shadow-sm border-b",
44
+ className
45
+ )}
46
+ data-slot="profile-user-tabs"
47
+ >
48
+ {#if isLoading}
49
+ {#each tabs as tab (tab.id)}
50
+ <Skeleton class="h-9 w-32 shrink-0 rounded-t-md" />
51
+ {/each}
52
+ {:else}
53
+ {#each tabs as tab (tab.id)}
54
+ <button
55
+ type="button"
56
+ onclick={() => handleSelect(tab.id)}
57
+ class={cn(
58
+ "shrink-0 px-4 py-2 text-sm font-medium transition-colors whitespace-nowrap",
59
+ style === "underline"
60
+ ? cn(
61
+ "border-b-2",
62
+ currentTab === tab.id
63
+ ? "border-foreground text-foreground"
64
+ : "border-transparent text-muted-foreground hover:text-foreground"
65
+ )
66
+ : cn(
67
+ "rounded-md",
68
+ currentTab === tab.id
69
+ ? "bg-primary/10 text-primary"
70
+ : "text-muted-foreground hover:bg-muted hover:text-foreground"
71
+ )
72
+ )}
73
+ >
74
+ {tab.label}
75
+ </button>
76
+ {/each}
77
+ {/if}
78
+ </div>
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Barra de tabs reutilizável para as variantes de perfil.
3
+ * Suporta loading state, tabs customizadas, active tab controlável
4
+ * e estilos variantes (underline vs pill).
5
+ * @component
6
+ */
7
+ export type ProfileUserTabItem = {
8
+ id: string;
9
+ label: string;
10
+ };
11
+ export type ProfileUserTabsStyle = "underline" | "pill";
12
+ type $$ComponentProps = {
13
+ tabs: ProfileUserTabItem[];
14
+ activeTab?: string;
15
+ style?: ProfileUserTabsStyle;
16
+ isLoading?: boolean;
17
+ className?: string;
18
+ onchange?: (id: string) => void;
19
+ };
20
+ declare const ProfileUserTabs: import("svelte").Component<$$ComponentProps, {}, "activeTab">;
21
+ type ProfileUserTabs = ReturnType<typeof ProfileUserTabs>;
22
+ export default ProfileUserTabs;