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.
- package/dist/components/core/card/product/01/CardProduct01.svelte +130 -0
- package/dist/components/core/card/product/01/CardProduct01.svelte.d.ts +4 -0
- package/dist/components/core/card/product/02/CardProduct02.svelte +118 -0
- package/dist/components/core/card/product/02/CardProduct02.svelte.d.ts +4 -0
- package/dist/components/core/card/product/CardProduct.svelte +22 -0
- package/dist/components/core/card/product/CardProduct.svelte.d.ts +11 -0
- package/dist/components/core/card/product/data.d.ts +3 -0
- package/dist/components/core/card/product/data.js +89 -0
- package/dist/components/core/card/profile/01/CardProfile01.svelte +0 -1
- package/dist/components/core/card/promotion/CardPromotion.svelte +1 -1
- package/dist/components/core/card/promotion/CardPromotionPrice.svelte.d.ts +8 -0
- package/dist/components/core/card/shared/CardPhoneOrWhatsapp.svelte +12 -5
- package/dist/components/core/card/shared/CardTags.svelte +49 -24
- package/dist/components/core/card/shared/CardTitlePrice.svelte +24 -0
- package/dist/components/core/card/shared/CardTitlePrice.svelte.d.ts +15 -0
- package/dist/components/core/card/shared/TruncatableText.svelte +1 -1
- package/dist/components/core/card/types.d.ts +48 -0
- package/dist/components/core/carousel/grid/media/ui/CarouselGridMedia.svelte +38 -28
- package/dist/components/core/carousel/grid/product/types.d.ts +22 -0
- package/dist/components/core/carousel/grid/product/types.js +1 -0
- package/dist/components/core/carousel/grid/product/ui/CarouselGridProduct.svelte +71 -0
- package/dist/components/core/carousel/grid/product/ui/CarouselGridProduct.svelte.d.ts +4 -0
- package/dist/components/core/carousel/grid/profile/ui/CarouselGridProfile.svelte +56 -46
- package/dist/components/core/carousel/grid/promotion/types.d.ts +23 -0
- package/dist/components/core/carousel/grid/promotion/types.js +1 -0
- package/dist/components/core/carousel/grid/promotion/ui/CarouselGridPromotion.svelte +68 -0
- package/dist/components/core/carousel/grid/promotion/ui/CarouselGridPromotion.svelte.d.ts +4 -0
- package/dist/components/core/carousel/highlights/ui/CarouselHighlights.svelte +57 -47
- package/dist/components/core/carousel/product/types.d.ts +21 -0
- package/dist/components/core/carousel/product/types.js +1 -0
- package/dist/components/core/carousel/product/ui/CarouselProduct.svelte +57 -0
- package/dist/components/core/carousel/product/ui/CarouselProduct.svelte.d.ts +4 -0
- package/dist/components/core/carousel/promotion/ui/CarouselPromotion.svelte +57 -47
- package/dist/components/core/panel/NotFoundEmpty.svelte +76 -0
- package/dist/components/core/panel/NotFoundEmpty.svelte.d.ts +16 -0
- package/dist/components/pages/company-profile/CompanyProfile.svelte +144 -0
- package/dist/components/pages/company-profile/CompanyProfile.svelte.d.ts +4 -0
- package/dist/components/pages/company-profile/data.d.ts +2 -0
- package/dist/components/pages/company-profile/data.js +115 -0
- package/dist/components/pages/company-profile/shared/ProfileAboutCompany.svelte +79 -0
- package/dist/components/pages/company-profile/shared/ProfileAboutCompany.svelte.d.ts +9 -0
- package/dist/components/pages/company-profile/shared/ProfileBanner.svelte +270 -0
- package/dist/components/pages/company-profile/shared/ProfileBanner.svelte.d.ts +14 -0
- package/dist/components/pages/company-profile/shared/ProfileGallery.svelte +198 -0
- package/dist/components/pages/company-profile/shared/ProfileGallery.svelte.d.ts +9 -0
- package/dist/components/pages/company-profile/shared/ProfileMenuBadge.svelte +56 -0
- package/dist/components/pages/company-profile/shared/ProfileMenuBadge.svelte.d.ts +11 -0
- package/dist/components/pages/company-profile/shared/ProfileProducts.svelte +62 -0
- package/dist/components/pages/company-profile/shared/ProfileProducts.svelte.d.ts +9 -0
- package/dist/components/pages/company-profile/shared/ProfileSidebar.svelte +57 -0
- package/dist/components/pages/company-profile/shared/ProfileSidebar.svelte.d.ts +11 -0
- package/dist/components/pages/company-profile/types.d.ts +137 -0
- package/dist/components/pages/company-profile/types.js +1 -0
- package/dist/components/pages/privacy-policy-or-terms-of-use/data.d.ts +3 -0
- package/dist/components/pages/privacy-policy-or-terms-of-use/data.js +179 -0
- package/dist/components/pages/privacy-policy-or-terms-of-use/shared/DocSectionView.svelte +2 -1
- package/dist/components/pages/privacy-policy-or-terms-of-use/shared/DocSectionView.svelte.d.ts +1 -0
- package/dist/components/pages/product-details/01/ProductDetails01.svelte +19 -7
- package/dist/components/pages/product-details/02/ProductDetails02.svelte +42 -37
- package/dist/components/pages/product-details/03/ProductDetails03.svelte +33 -29
- package/dist/components/pages/product-details/ProductDetails.svelte +11 -7
- package/dist/components/pages/product-details/shared/ProductDetailFeatures.svelte +11 -5
- package/dist/components/pages/product-details/shared/ProductDetailFeatures.svelte.d.ts +2 -1
- package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte +52 -20
- package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte.d.ts +1 -1
- package/dist/components/pages/product-details/shared/ProductDetailImage.svelte +51 -20
- package/dist/components/pages/product-details/shared/ProductDetailImage.svelte.d.ts +1 -1
- package/dist/components/pages/product-details/shared/ProductDetailPrice.svelte +1 -1
- package/dist/components/pages/product-details/types.d.ts +9 -2
- package/dist/components/pages/profile-user/types.d.ts +7 -0
- package/dist/components/ui/empty/empty-content.svelte +23 -0
- package/dist/components/ui/empty/empty-content.svelte.d.ts +5 -0
- package/dist/components/ui/empty/empty-description.svelte +23 -0
- package/dist/components/ui/empty/empty-description.svelte.d.ts +5 -0
- package/dist/components/ui/empty/empty-header.svelte +20 -0
- package/dist/components/ui/empty/empty-header.svelte.d.ts +5 -0
- package/dist/components/ui/empty/empty-media.svelte +41 -0
- package/dist/components/ui/empty/empty-media.svelte.d.ts +26 -0
- package/dist/components/ui/empty/empty-title.svelte +20 -0
- package/dist/components/ui/empty/empty-title.svelte.d.ts +5 -0
- package/dist/components/ui/empty/empty.svelte +23 -0
- package/dist/components/ui/empty/empty.svelte.d.ts +5 -0
- package/dist/components/ui/empty/index.d.ts +7 -0
- package/dist/components/ui/empty/index.js +9 -0
- package/dist/globals.css +301 -5
- package/dist/i18n/langs/en.d.ts +25 -0
- package/dist/i18n/langs/en.js +25 -0
- package/dist/i18n/langs/pt.d.ts +25 -0
- package/dist/i18n/langs/pt.js +25 -0
- package/dist/i18n/translations.d.ts +50 -0
- package/package.json +1 -1
- package/dist/components/core/card/shared/CardStartPrice.svelte.d.ts +0 -8
- package/dist/components/pages/product-details/04/ProductDetails04.svelte +0 -65
- package/dist/components/pages/product-details/04/ProductDetails04.svelte.d.ts +0 -11
- /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
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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,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;
|