negodesign 0.0.35 → 0.0.37

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 (95) hide show
  1. package/dist/components/core/card/product/01/CardProduct01.svelte +130 -0
  2. package/dist/components/core/card/product/01/CardProduct01.svelte.d.ts +4 -0
  3. package/dist/components/core/card/product/02/CardProduct02.svelte +118 -0
  4. package/dist/components/core/card/product/02/CardProduct02.svelte.d.ts +4 -0
  5. package/dist/components/core/card/product/CardProduct.svelte +22 -0
  6. package/dist/components/core/card/product/CardProduct.svelte.d.ts +11 -0
  7. package/dist/components/core/card/product/data.d.ts +3 -0
  8. package/dist/components/core/card/product/data.js +89 -0
  9. package/dist/components/core/card/profile/01/CardProfile01.svelte +0 -1
  10. package/dist/components/core/card/promotion/CardPromotion.svelte +1 -1
  11. package/dist/components/core/card/promotion/CardPromotionPrice.svelte.d.ts +8 -0
  12. package/dist/components/core/card/shared/CardPhoneOrWhatsapp.svelte +12 -5
  13. package/dist/components/core/card/shared/CardTags.svelte +49 -24
  14. package/dist/components/core/card/shared/CardTitlePrice.svelte +24 -0
  15. package/dist/components/core/card/shared/CardTitlePrice.svelte.d.ts +15 -0
  16. package/dist/components/core/card/shared/TruncatableText.svelte +1 -1
  17. package/dist/components/core/card/types.d.ts +48 -0
  18. package/dist/components/core/carousel/grid/media/ui/CarouselGridMedia.svelte +38 -28
  19. package/dist/components/core/carousel/grid/product/types.d.ts +22 -0
  20. package/dist/components/core/carousel/grid/product/types.js +1 -0
  21. package/dist/components/core/carousel/grid/product/ui/CarouselGridProduct.svelte +71 -0
  22. package/dist/components/core/carousel/grid/product/ui/CarouselGridProduct.svelte.d.ts +4 -0
  23. package/dist/components/core/carousel/grid/profile/ui/CarouselGridProfile.svelte +56 -46
  24. package/dist/components/core/carousel/grid/promotion/types.d.ts +23 -0
  25. package/dist/components/core/carousel/grid/promotion/types.js +1 -0
  26. package/dist/components/core/carousel/grid/promotion/ui/CarouselGridPromotion.svelte +68 -0
  27. package/dist/components/core/carousel/grid/promotion/ui/CarouselGridPromotion.svelte.d.ts +4 -0
  28. package/dist/components/core/carousel/highlights/ui/CarouselHighlights.svelte +57 -47
  29. package/dist/components/core/carousel/product/types.d.ts +21 -0
  30. package/dist/components/core/carousel/product/types.js +1 -0
  31. package/dist/components/core/carousel/product/ui/CarouselProduct.svelte +57 -0
  32. package/dist/components/core/carousel/product/ui/CarouselProduct.svelte.d.ts +4 -0
  33. package/dist/components/core/carousel/promotion/ui/CarouselPromotion.svelte +57 -47
  34. package/dist/components/core/panel/NotFoundEmpty.svelte +76 -0
  35. package/dist/components/core/panel/NotFoundEmpty.svelte.d.ts +16 -0
  36. package/dist/components/pages/company-profile/CompanyProfile.svelte +144 -0
  37. package/dist/components/pages/company-profile/CompanyProfile.svelte.d.ts +4 -0
  38. package/dist/components/pages/company-profile/data.d.ts +2 -0
  39. package/dist/components/pages/company-profile/data.js +115 -0
  40. package/dist/components/pages/company-profile/shared/ProfileAboutCompany.svelte +79 -0
  41. package/dist/components/pages/company-profile/shared/ProfileAboutCompany.svelte.d.ts +9 -0
  42. package/dist/components/pages/company-profile/shared/ProfileBanner.svelte +270 -0
  43. package/dist/components/pages/company-profile/shared/ProfileBanner.svelte.d.ts +14 -0
  44. package/dist/components/pages/company-profile/shared/ProfileGallery.svelte +198 -0
  45. package/dist/components/pages/company-profile/shared/ProfileGallery.svelte.d.ts +9 -0
  46. package/dist/components/pages/company-profile/shared/ProfileMenuBadge.svelte +56 -0
  47. package/dist/components/pages/company-profile/shared/ProfileMenuBadge.svelte.d.ts +11 -0
  48. package/dist/components/pages/company-profile/shared/ProfileProducts.svelte +62 -0
  49. package/dist/components/pages/company-profile/shared/ProfileProducts.svelte.d.ts +9 -0
  50. package/dist/components/pages/company-profile/shared/ProfileSidebar.svelte +57 -0
  51. package/dist/components/pages/company-profile/shared/ProfileSidebar.svelte.d.ts +11 -0
  52. package/dist/components/pages/company-profile/types.d.ts +137 -0
  53. package/dist/components/pages/company-profile/types.js +1 -0
  54. package/dist/components/pages/privacy-policy-or-terms-of-use/data.d.ts +3 -0
  55. package/dist/components/pages/privacy-policy-or-terms-of-use/data.js +179 -0
  56. package/dist/components/pages/privacy-policy-or-terms-of-use/shared/DocSectionView.svelte +2 -1
  57. package/dist/components/pages/privacy-policy-or-terms-of-use/shared/DocSectionView.svelte.d.ts +1 -0
  58. package/dist/components/pages/product-details/01/ProductDetails01.svelte +19 -7
  59. package/dist/components/pages/product-details/02/ProductDetails02.svelte +42 -37
  60. package/dist/components/pages/product-details/03/ProductDetails03.svelte +33 -29
  61. package/dist/components/pages/product-details/ProductDetails.svelte +11 -7
  62. package/dist/components/pages/product-details/shared/ProductDetailFeatures.svelte +11 -5
  63. package/dist/components/pages/product-details/shared/ProductDetailFeatures.svelte.d.ts +2 -1
  64. package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte +52 -20
  65. package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte.d.ts +1 -1
  66. package/dist/components/pages/product-details/shared/ProductDetailImage.svelte +51 -20
  67. package/dist/components/pages/product-details/shared/ProductDetailImage.svelte.d.ts +1 -1
  68. package/dist/components/pages/product-details/shared/ProductDetailPrice.svelte +1 -1
  69. package/dist/components/pages/product-details/types.d.ts +9 -2
  70. package/dist/components/pages/profile-user/types.d.ts +7 -0
  71. package/dist/components/ui/empty/empty-content.svelte +23 -0
  72. package/dist/components/ui/empty/empty-content.svelte.d.ts +5 -0
  73. package/dist/components/ui/empty/empty-description.svelte +23 -0
  74. package/dist/components/ui/empty/empty-description.svelte.d.ts +5 -0
  75. package/dist/components/ui/empty/empty-header.svelte +20 -0
  76. package/dist/components/ui/empty/empty-header.svelte.d.ts +5 -0
  77. package/dist/components/ui/empty/empty-media.svelte +41 -0
  78. package/dist/components/ui/empty/empty-media.svelte.d.ts +26 -0
  79. package/dist/components/ui/empty/empty-title.svelte +20 -0
  80. package/dist/components/ui/empty/empty-title.svelte.d.ts +5 -0
  81. package/dist/components/ui/empty/empty.svelte +23 -0
  82. package/dist/components/ui/empty/empty.svelte.d.ts +5 -0
  83. package/dist/components/ui/empty/index.d.ts +7 -0
  84. package/dist/components/ui/empty/index.js +9 -0
  85. package/dist/globals.css +301 -5
  86. package/dist/i18n/langs/en.d.ts +25 -0
  87. package/dist/i18n/langs/en.js +25 -0
  88. package/dist/i18n/langs/pt.d.ts +25 -0
  89. package/dist/i18n/langs/pt.js +25 -0
  90. package/dist/i18n/translations.d.ts +50 -0
  91. package/package.json +1 -1
  92. package/dist/components/core/card/shared/CardStartPrice.svelte.d.ts +0 -8
  93. package/dist/components/pages/product-details/04/ProductDetails04.svelte +0 -65
  94. package/dist/components/pages/product-details/04/ProductDetails04.svelte.d.ts +0 -11
  95. /package/dist/components/core/card/{shared/CardStartPrice.svelte → promotion/CardPromotionPrice.svelte} +0 -0
@@ -1,47 +1,57 @@
1
- <script lang="ts">
2
- import CardPromotion from "../../../card/promotion/CardPromotion.svelte";
3
- import CarouselSlot from "../../../panel/CarouselSlot.svelte";
4
- import * as Carousel from "../../../../ui/carousel/index.js";
5
- import type { CarouselPromotionProps } from "../types";
6
- import CarouselHeader from "../../shared/ui/CarouselHeader.svelte";
7
- import { useDevice } from "../../../../../hooks/responsive.svelte";
8
- import Autoplay from "embla-carousel-autoplay";
9
-
10
- const {
11
- headerProps,
12
- slotProps,
13
- items,
14
- isLoading = false,
15
- isDescriptionIcon,
16
- isDescriptionLabel,
17
- }: CarouselPromotionProps = $props();
18
-
19
- const responsive = useDevice();
20
- </script>
21
-
22
- <CarouselHeader {...headerProps}>
23
- <CarouselSlot
24
- {...slotProps}
25
- containerClass="w-full"
26
- plugins={[Autoplay({ delay: 4000, stopOnInteraction: true })]}
27
- >
28
- {#if isLoading}
29
- {#each Array.from( { length: responsive.isMobile ? 5 : 10 }, ) as _, i (`loading-${i}`)}
30
- <Carousel.Item class="pl-5 w-75 basis-auto">
31
- <CardPromotion
32
- id={i}
33
- {isLoading}
34
- {isDescriptionIcon}
35
- {isDescriptionLabel}
36
- />
37
- </Carousel.Item>
38
- {/each}
39
- {:else}
40
- {#each items as item, i (`promotion-${item.id ?? i}`)}
41
- <Carousel.Item class="pl-2 w-75 basis-auto">
42
- <CardPromotion {...item} {isDescriptionIcon} {isDescriptionLabel} />
43
- </Carousel.Item>
44
- {/each}
45
- {/if}
46
- </CarouselSlot>
47
- </CarouselHeader>
1
+ <script lang="ts">
2
+ import CardPromotion from "../../../card/promotion/CardPromotion.svelte";
3
+ import CarouselSlot from "../../../panel/CarouselSlot.svelte";
4
+ import NotFoundEmpty from "../../../panel/NotFoundEmpty.svelte";
5
+ import * as Carousel from "../../../../ui/carousel/index.js";
6
+ import type { CarouselPromotionProps } from "../types";
7
+ import CarouselHeader from "../../shared/ui/CarouselHeader.svelte";
8
+ import { useDevice } from "../../../../../hooks/responsive.svelte";
9
+ import { t } from "../../../../../i18n";
10
+ import Autoplay from "embla-carousel-autoplay";
11
+
12
+ const {
13
+ headerProps,
14
+ slotProps,
15
+ items,
16
+ isLoading = false,
17
+ isDescriptionIcon,
18
+ isDescriptionLabel,
19
+ }: CarouselPromotionProps = $props();
20
+
21
+ const responsive = useDevice();
22
+ const isEmpty = $derived(!isLoading && (!items || items.length === 0));
23
+ </script>
24
+
25
+ <CarouselHeader {...headerProps}>
26
+ {#if isEmpty}
27
+ <NotFoundEmpty
28
+ title={$t("empty.promotions.title")}
29
+ description={$t("empty.promotions.description")}
30
+ />
31
+ {:else}
32
+ <CarouselSlot
33
+ {...slotProps}
34
+ containerClass="w-full"
35
+ plugins={[Autoplay({ delay: 4000, stopOnInteraction: true })]}
36
+ >
37
+ {#if isLoading}
38
+ {#each Array.from( { length: responsive.isMobile ? 5 : 10 }, ) as _, i (`loading-${i}`)}
39
+ <Carousel.Item class="pl-5 w-75 basis-auto">
40
+ <CardPromotion
41
+ id={i}
42
+ {isLoading}
43
+ {isDescriptionIcon}
44
+ {isDescriptionLabel}
45
+ />
46
+ </Carousel.Item>
47
+ {/each}
48
+ {:else}
49
+ {#each items as item, i (`promotion-${item.id ?? i}`)}
50
+ <Carousel.Item class="pl-2 w-75 basis-auto">
51
+ <CardPromotion {...item} {isDescriptionIcon} {isDescriptionLabel} />
52
+ </Carousel.Item>
53
+ {/each}
54
+ {/if}
55
+ </CarouselSlot>
56
+ {/if}
57
+ </CarouselHeader>
@@ -0,0 +1,76 @@
1
+ <script lang="ts">
2
+ import * as Empty from "../../ui/empty/index.js";
3
+ import { t } from "../../../i18n";
4
+ import { HugeiconsIcon } from "@hugeicons/svelte";
5
+ import { Search01Icon } from "@hugeicons/core-free-icons";
6
+ import type { Snippet } from "svelte";
7
+ import type { IconSvgElement } from "@hugeicons/svelte";
8
+
9
+ type Props = {
10
+ title?: string;
11
+ description?: string;
12
+ icon?: IconSvgElement;
13
+ className?: string;
14
+ actionHref?: string;
15
+ actionLabel?: string;
16
+ onAction?: () => void;
17
+ showAction?: boolean;
18
+ children?: Snippet;
19
+ };
20
+
21
+ let {
22
+ title,
23
+ description,
24
+ icon = Search01Icon,
25
+ className = "",
26
+ actionHref = "#/",
27
+ actionLabel,
28
+ onAction,
29
+ showAction = false,
30
+ children,
31
+ }: Props = $props();
32
+
33
+ const resolvedTitle = $derived(title ?? $t("empty.title"));
34
+ const resolvedDescription = $derived(description ?? $t("empty.description"));
35
+ </script>
36
+
37
+ <Empty.Root class="w-full py-8 {className}">
38
+ <Empty.Header>
39
+ {#if icon}
40
+ <Empty.Media variant="icon">
41
+ <HugeiconsIcon {icon} class="size-6 text-muted-foreground" />
42
+ </Empty.Media>
43
+ {/if}
44
+ <Empty.Title class="text-base font-semibold">
45
+ {resolvedTitle}
46
+ </Empty.Title>
47
+ <Empty.Description class="text-sm text-muted-foreground max-w-sm">
48
+ {resolvedDescription}
49
+ </Empty.Description>
50
+ </Empty.Header>
51
+
52
+ {#if children}
53
+ <Empty.Content>
54
+ {@render children()}
55
+ </Empty.Content>
56
+ {:else if showAction || onAction}
57
+ <Empty.Content>
58
+ {#if onAction}
59
+ <button
60
+ type="button"
61
+ onclick={onAction}
62
+ class="text-xs font-medium text-primary hover:underline"
63
+ >
64
+ {actionLabel ?? $t("empty.contact_support")}
65
+ </button>
66
+ {:else}
67
+ <Empty.Description class="text-xs">
68
+ {$t("empty.support")}
69
+ <a href={actionHref} class="text-primary hover:underline font-medium ml-1">
70
+ {actionLabel ?? $t("empty.contact_support")}
71
+ </a>
72
+ </Empty.Description>
73
+ {/if}
74
+ </Empty.Content>
75
+ {/if}
76
+ </Empty.Root>
@@ -0,0 +1,16 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { IconSvgElement } from "@hugeicons/svelte";
3
+ type Props = {
4
+ title?: string;
5
+ description?: string;
6
+ icon?: IconSvgElement;
7
+ className?: string;
8
+ actionHref?: string;
9
+ actionLabel?: string;
10
+ onAction?: () => void;
11
+ showAction?: boolean;
12
+ children?: Snippet;
13
+ };
14
+ declare const NotFoundEmpty: import("svelte").Component<Props, {}, "">;
15
+ type NotFoundEmpty = ReturnType<typeof NotFoundEmpty>;
16
+ export default NotFoundEmpty;
@@ -0,0 +1,144 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Página de perfil da empresa/fábrica.
4
+ * Combina o banner de apresentação, navegação entre secções
5
+ * (Sidebar em desktop, badges em mobile) e as secções
6
+ * "Sobre a Empresa" e "Galeria".
7
+ *
8
+ * @component
9
+ * @example
10
+ * ```svelte
11
+ * <CompanyProfile data={companyData} />
12
+ *
13
+ * <CompanyProfile
14
+ * data={companyData}
15
+ * onContact={(id) => openWhatsapp(id)}
16
+ * onShare={(id) => navigator.share?.({ url: shareUrl(id) })}
17
+ * />
18
+ * ```
19
+ */
20
+ </script>
21
+
22
+ <script lang="ts">
23
+ import { onMount } from "svelte";
24
+ import { useDevice } from "../../../hooks/responsive.svelte";
25
+ import ProfileBanner from "./shared/ProfileBanner.svelte";
26
+ import ProfileSidebar from "./shared/ProfileSidebar.svelte";
27
+ import ProfileMenuBadge from "./shared/ProfileMenuBadge.svelte";
28
+ import ProfileAboutCompany from "./shared/ProfileAboutCompany.svelte";
29
+ import ProfileGallery from "./shared/ProfileGallery.svelte";
30
+ import ProfileProducts from "./shared/ProfileProducts.svelte";
31
+ import { defaultCompanyProfileData } from "./data";
32
+ import type { CompanyProfileProps, ProfileNavItem } from "./types";
33
+
34
+ let {
35
+ data = defaultCompanyProfileData,
36
+ isLoading = false,
37
+ onEmail,
38
+ onWhatsapp,
39
+ onCopyLink,
40
+ onShare,
41
+ }: CompanyProfileProps = $props();
42
+
43
+ const responsive = useDevice();
44
+
45
+ const sections: ProfileNavItem[] = [
46
+ { id: "banner", label: "Apresentação" },
47
+ { id: "sobre-empresa", label: "Sobre" },
48
+ { id: "galeria-empresa", label: "Galeria" },
49
+ { id: "products-empresa", label: "Produtos" },
50
+ ];
51
+
52
+ let activeSection = $state(sections[0]?.id);
53
+ let sectionEls: Record<string, HTMLElement> = {};
54
+
55
+ function scrollToSection(id: string) {
56
+ activeSection = id;
57
+ sectionEls[id]?.scrollIntoView({ behavior: "smooth", block: "start" });
58
+ }
59
+
60
+ onMount(() => {
61
+ const observer = new IntersectionObserver(
62
+ (entries) => {
63
+ const visible = entries
64
+ .filter((entry) => entry.isIntersecting)
65
+ .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
66
+ if (
67
+ visible[0]?.target instanceof HTMLElement &&
68
+ visible[0].target.dataset.sectionId
69
+ ) {
70
+ activeSection = visible[0].target.dataset.sectionId;
71
+ }
72
+ },
73
+ { rootMargin: "-45% 0px -45% 0px", threshold: 0 },
74
+ );
75
+
76
+ Object.values(sectionEls).forEach((el) => el && observer.observe(el));
77
+
78
+ return () => observer.disconnect();
79
+ });
80
+ </script>
81
+
82
+ <div class="">
83
+ {#if responsive.isMobile}
84
+ <ProfileMenuBadge
85
+ items={sections}
86
+ active={activeSection}
87
+ onSelect={scrollToSection}
88
+ className="mt-6"
89
+ />
90
+ {/if}
91
+
92
+ <div class="flex gap-6">
93
+ {#if !responsive.isMobile}
94
+ <ProfileSidebar
95
+ items={sections}
96
+ active={activeSection}
97
+ onSelect={scrollToSection}
98
+ />
99
+ {/if}
100
+
101
+ <div class="flex min-w-0 flex-1 flex-col gap-6 md:px-5 md:pl-10 lg:pl-12 pb-5 md:pb-10">
102
+ <div
103
+ id="banner"
104
+ bind:this={sectionEls["banner"]}
105
+ data-section-id="banner"
106
+ class="p-3"
107
+ >
108
+ <ProfileBanner
109
+ id={data.id}
110
+ data={data.banner}
111
+ {isLoading}
112
+ {onEmail}
113
+ {onWhatsapp}
114
+ {onCopyLink}
115
+ {onShare}
116
+ />
117
+ </div>
118
+
119
+ <div
120
+ id="sobre-empresa"
121
+ bind:this={sectionEls["sobre-empresa"]}
122
+ data-section-id="sobre-empresa"
123
+ >
124
+ <ProfileAboutCompany data={data.about} {isLoading} />
125
+ </div>
126
+
127
+ <div
128
+ id="galeria-empresa"
129
+ bind:this={sectionEls["galeria-empresa"]}
130
+ data-section-id="galeria-empresa"
131
+ >
132
+ <ProfileGallery data={data.gallery} {isLoading} />
133
+ </div>
134
+
135
+ <div
136
+ id="products-empresa"
137
+ bind:this={sectionEls["products-empresa"]}
138
+ data-section-id="products-empresa"
139
+ >
140
+ <ProfileProducts data={data.products} {isLoading} />
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { CompanyProfileProps } from "./types";
2
+ declare const CompanyProfile: import("svelte").Component<CompanyProfileProps, {}, "">;
3
+ type CompanyProfile = ReturnType<typeof CompanyProfile>;
4
+ export default CompanyProfile;
@@ -0,0 +1,2 @@
1
+ import type { CompanyProfileData } from "./types";
2
+ export declare const defaultCompanyProfileData: CompanyProfileData;
@@ -0,0 +1,115 @@
1
+ import ImgPlaceholder from "../../../assets/placeholder-image.png";
2
+ import VideoPlaceholder from "../../../assets/lonely-404.mp4";
3
+ export const defaultCompanyProfileData = {
4
+ id: "cartaplast",
5
+ banner: {
6
+ coverImages: [
7
+ {
8
+ src: "https://images.unsplash.com/photo-1586528116311-ad8dd3c8310d?q=80&w=1200&auto=format&fit=crop",
9
+ alt: "Armazém de bobinas e logística Cartaplast",
10
+ },
11
+ {
12
+ src: "https://images.unsplash.com/photo-1581091226825-a6a2a5aee158?q=80&w=1200&auto=format&fit=crop",
13
+ alt: "Linha de produção industrial de embalagens",
14
+ },
15
+ ],
16
+ logo: "https://images.unsplash.com/photo-1560179707-f14e90ef3623?q=80&w=300&auto=format&fit=crop",
17
+ name: "Hotel empanados S.A",
18
+ countryLabel: "Angola",
19
+ countryFlag: "🇦🇴",
20
+ contactLabel: "Contactar fábrica",
21
+ stats: [
22
+ { label: "Setor de atuação", value: "Papel e Embalagens" },
23
+ { label: "Anos de Atuação", value: "8+ Anos" },
24
+ { label: "Número de Funcionários", value: "51 - 100 Funcionários" },
25
+ { label: "Localização", value: "Polo Industrial da Catumbela, Benguela" },
26
+ ],
27
+ },
28
+ about: {
29
+ image: "https://images.unsplash.com/photo-1504917599217-d4dc5ebe6122?q=80&w=1200&auto=format&fit=crop",
30
+ imageAlt: "Instalações industriais da fábrica Cartaplast",
31
+ title: "Sobre a CARTAPLAST",
32
+ paragraphs: [
33
+ "A Cartaplast é uma empresa de referência em Angola, especializada na fabricação de cartão canelado, caixas de papelão e soluções sustentáveis de embalagens industriais, localizada estrategicamente no Polo de Desenvolvimento Industrial da Catumbela (PDIC), na província de Benguela.",
34
+ "A nossa capacidade instalada e tecnologia de ponta proporcionam total flexibilidade na produção de múltiplos modelos e dimensões de caixas com impressão personalizada de alta precisão, agregando valor e identidade à marca de cada parceiro.",
35
+ "Com rigorosos padrões de controlo de qualidade e foco na sustentabilidade, atendemos com excelência os setores da Indústria Pesqueira, Agroalimentar, Avicultura, Bebidas, Farmacêutica, Distribuição e Comércio Geral em todo o país.",
36
+ ],
37
+ },
38
+ gallery: {
39
+ title: "Galeria",
40
+ images: [
41
+ {
42
+ src: "https://images.unsplash.com/photo-1581091226825-a6a2a5aee158?q=80&w=1200&auto=format&fit=crop",
43
+ alt: "Linha de produção automatizada de caixas",
44
+ type: "image",
45
+ },
46
+ {
47
+ src: VideoPlaceholder,
48
+ alt: "Vídeo institucional e processo de fabrico",
49
+ type: "video",
50
+ },
51
+ {
52
+ src: "https://images.unsplash.com/photo-1586528116311-ad8dd3c8310d?q=80&w=1200&auto=format&fit=crop",
53
+ alt: "Armazém de matéria-prima e bobinas de papel",
54
+ type: "image",
55
+ },
56
+ {
57
+ src: "https://images.unsplash.com/photo-1504917599217-d4dc5ebe6122?q=80&w=1200&auto=format&fit=crop",
58
+ alt: "Maquinaria de corte e vincagem",
59
+ type: "image",
60
+ },
61
+ {
62
+ src: "https://images.unsplash.com/photo-1587293852726-70cdb56c2866?q=80&w=1200&auto=format&fit=crop",
63
+ alt: "Área de expedição e embalamento final",
64
+ type: "image",
65
+ },
66
+ ],
67
+ },
68
+ products: {
69
+ title: "Produtos e Soluções",
70
+ description: "Conheça as principais soluções de embalagens e produtos industriais fabricados sob medida.",
71
+ variant: 1,
72
+ items: [
73
+ {
74
+ id: "prod-1",
75
+ title: "Caixas de Papelão Canelado",
76
+ content: "Embalagens resistentes de alta durabilidade para transporte industrial e exportação.",
77
+ price: "12.500 Kz",
78
+ imageUrl: "https://images.unsplash.com/photo-1586528116311-ad8dd3c8310d?q=80&w=600&auto=format&fit=crop",
79
+ buttonBuyText: "Solicitar Cotação",
80
+ onClickBuy: (id) => alert(`Solicitar produto ${id}`),
81
+ tags: [{ text: "Industrial" }, { text: "Canelado" }],
82
+ },
83
+ {
84
+ id: "prod-2",
85
+ title: "Bobinas de Papel Kraft",
86
+ content: "Bobinas industriais de papel kraft para empacotamento e proteção de mercadorias.",
87
+ price: "35.000 Kz",
88
+ imageUrl: "https://images.unsplash.com/photo-1581091226825-a6a2a5aee158?q=80&w=600&auto=format&fit=crop",
89
+ buttonBuyText: "Solicitar Cotação",
90
+ onClickBuy: (id) => alert(`Solicitar produto ${id}`),
91
+ tags: [{ text: "Kraft" }, { text: "Sustentável" }],
92
+ },
93
+ {
94
+ id: "prod-3",
95
+ title: "Caixas para Setor Alimentar",
96
+ content: "Embalagens certificadas para conservação e distribuição de alimentos frescos e congelados.",
97
+ price: "18.000 Kz",
98
+ imageUrl: "https://images.unsplash.com/photo-1504917599217-d4dc5ebe6122?q=80&w=600&auto=format&fit=crop",
99
+ buttonBuyText: "Solicitar Cotação",
100
+ onClickBuy: (id) => alert(`Solicitar produto ${id}`),
101
+ tags: [{ text: "Alimentar" }, { text: "Certificado" }],
102
+ },
103
+ {
104
+ id: "prod-4",
105
+ title: "Embalagens Personalizadas",
106
+ content: "Impressão flexográfica personalizada em caixas de todos os formatos e medidas.",
107
+ price: "22.000 Kz",
108
+ imageUrl: "https://images.unsplash.com/photo-1587293852726-70cdb56c2866?q=80&w=600&auto=format&fit=crop",
109
+ buttonBuyText: "Solicitar Cotação",
110
+ onClickBuy: (id) => alert(`Solicitar produto ${id}`),
111
+ tags: [{ text: "Personalizado" }, { text: "Design" }],
112
+ },
113
+ ],
114
+ },
115
+ };
@@ -0,0 +1,79 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Secção "Sobre a Empresa": imagem ilustrativa e parágrafos descritivos.
4
+ * Quando `isLoading` é true, exibe Skeletons.
5
+ * @component
6
+ * @example
7
+ * ```svelte
8
+ * <ProfileAboutCompany data={data.about} />
9
+ * ```
10
+ */
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
15
+ import ImgPlaceholder from "../../../../assets/placeholder-image.png";
16
+ import type { ProfileAboutData } from "../types";
17
+
18
+ let {
19
+ data,
20
+ isLoading = false,
21
+ className,
22
+ }: {
23
+ data: ProfileAboutData;
24
+ isLoading?: boolean;
25
+ className?: string;
26
+ } = $props();
27
+ </script>
28
+
29
+ <div class="bg-gray-50 dark:bg-background p-4 md:p-6 rounded-lg {className}">
30
+ <h2 class="mb-4 flex items-center gap-2 text-lg font-bold">
31
+ <svg
32
+ viewBox="0 0 24 24"
33
+ class="size-5 text-muted-foreground"
34
+ fill="none"
35
+ stroke="currentColor"
36
+ stroke-width="2"
37
+ stroke-linecap="round"
38
+ stroke-linejoin="round"
39
+ aria-hidden="true"
40
+ >
41
+ <path d="M3 21h18" />
42
+ <path d="M6 21V7l6-4 6 4v14" />
43
+ <path d="M10 21v-6h4v6" />
44
+ <path d="M10 11h.01M14 11h.01M10 15h.01M14 15h.01" />
45
+ </svg>
46
+ Sobre a Empresa
47
+ </h2>
48
+
49
+ {#if isLoading}
50
+ <div class="grid gap-6 md:grid-cols-2">
51
+ <Skeleton class="aspect-4/3 w-full rounded-lg" />
52
+ <div class="flex flex-col gap-3">
53
+ {#each Array.from({ length: 5 }) as _, i (i)}
54
+ <Skeleton class="h-4 w-full rounded-md" />
55
+ {/each}
56
+ </div>
57
+ </div>
58
+ {:else}
59
+ <div class="grid gap-6 md:grid-cols-2 md:items-start">
60
+ {#if data.image}
61
+ <div class="overflow-hidden rounded-lg border">
62
+ <img
63
+ src={data.image || ImgPlaceholder}
64
+ alt={data.imageAlt ?? data.title ?? "Sobre a empresa"}
65
+ class="aspect-4/3 w-full object-cover"
66
+ onerror={(e) =>
67
+ ((e.target as HTMLImageElement).src = ImgPlaceholder)}
68
+ />
69
+ </div>
70
+ {/if}
71
+
72
+ <div class="flex flex-col gap-4">
73
+ {#each data.paragraphs as paragraph, i (i)}
74
+ <p class="leading-7 text-foreground/80">{paragraph}</p>
75
+ {/each}
76
+ </div>
77
+ </div>
78
+ {/if}
79
+ </div>
@@ -0,0 +1,9 @@
1
+ import type { ProfileAboutData } from "../types";
2
+ type $$ComponentProps = {
3
+ data: ProfileAboutData;
4
+ isLoading?: boolean;
5
+ className?: string;
6
+ };
7
+ declare const ProfileAboutCompany: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type ProfileAboutCompany = ReturnType<typeof ProfileAboutCompany>;
9
+ export default ProfileAboutCompany;