Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix estimations #342

Merged
merged 11 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions components.198185.json
Original file line number Diff line number Diff line change
Expand Up @@ -2247,31 +2247,40 @@
"name": "landing-page",
"display_name": null,
"created_at": "2024-01-09T16:40:58.537Z",
"updated_at": "2024-01-16T10:35:21.911Z",
"updated_at": "2024-02-29T15:42:02.200Z",
"id": 5142044,
"schema": {
"keep_top_bar_hidden": {
"type": "boolean",
"pos": 0,
"required": false
},
"blocks": {
"type": "bloks",
"restrict_type": "groups",
"restrict_components": true,
"component_whitelist": [],
"component_group_whitelist": ["c9011224-9690-43bd-b686-e8f60ef6c7f6"]
"component_group_whitelist": ["c9011224-9690-43bd-b686-e8f60ef6c7f6"],
"pos": 1
},
"tab-577daf7c-b61e-4a23-8184-bd9c01ae0af3": {
"display_name": "SEO",
"keys": ["seo_title", "seo_description", "seo_og_image", "change_frequency", "priority"],
"pos": 0,
"pos": 2,
"type": "tab"
},
"seo_title": {
"type": "text"
"type": "text",
"pos": 3
},
"seo_description": {
"type": "text"
"type": "text",
"pos": 4
},
"seo_og_image": {
"type": "asset",
"filetypes": ["images"]
"filetypes": ["images"],
"pos": 5
},
"change_frequency": {
"type": "option",
Expand Down Expand Up @@ -2313,12 +2322,14 @@
"value": "never",
"name": "Never"
}
]
],
"pos": 6
},
"priority": {
"type": "number",
"description": "The priority of the page relative to other pages on your site, where 0.1 is the lowest priority and 1.0 is the highest priority.\n\n1.0-0.8\nHomepage, product information, landing pages.\n\n0.7-0.4\nNews articles, some weather services, blog posts, pages that no site would be complete without.\n\n0.3-0.0\nFAQs, outdated info, old press releases, completely static pages that are still relevant enough to keep from deleting entirely.",
"decimals": 1
"decimals": 1,
"pos": 7
}
},
"image": null,
Expand Down
249 changes: 151 additions & 98 deletions src/components/blocks/estimation.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,58 @@
import type { EstimationStoryblok } from '$types/bloks';
import IllustrationEmply from '../illustrations/assets/illustration-emply.webp';
import IllustrationEmplyLight from '../illustrations/assets/illustration-emply-light.webp';
import ArrowLight from '../illustrations/assets/arrow-estimations-white.webp';
import ArrowDark from '../illustrations/assets/arrow-estimations.webp';
import { theme } from '$lib/stores/theme';
import { Icon, MultiSelect } from '@significa/svelte-ui';
import { CheckboxGroup, Radio } from '@significa/svelte-ui';
import clsx from 'clsx';
import ContactForm from '$components/contact-form.svelte';
import { estimations } from '$lib/estimations';
import type { ListboxOption } from '@melt-ui/svelte/dist/builders/listbox/types';
import { estimations, estimationsCheckbox } from '$lib/estimations';

import HandAsset from '../illustrations/assets/hand.webp';
import PreFooterAsset from '../illustrations/assets/pre-footer.webp';
import PreFooterAssetLight from '../illustrations/assets/pre-footer-light.webp';

import { t } from '$lib/i18n';

export let block: EstimationStoryblok;

let open = false;

$: src = $theme === 'dark' ? IllustrationEmply : IllustrationEmplyLight;
$: srcArrow = $theme === 'dark' ? ArrowDark : ArrowLight;

let selected: Record<string, ListboxOption<string>[]> = {};
let selectedRadio: Array<string> = [];
let selectedCheckbox: Array<string> = [];

$: selectedMap = Object.entries(selected).map(([key, value]) => ({ key, value }));
$: selectedValuesToNotion = [
...estimations.flatMap((estimation, index) =>
estimation.options
.filter((option) => option.name === selectedRadio[index])
.map((option) => `${estimation.name} / ${option.name}`)
),
...estimationsCheckbox.flatMap((estimation) =>
estimation.options
.filter((option) => selectedCheckbox.includes(option.name))
.map((option) => `${estimation.name} / ${option.name}`)
)
].join(' , ');

$: selectedKeyValues = selectedMap.map(({ value }) => value.map((v) => v.value)).flat();
$: estimationsMapRadio = estimations
.map((v, i) => v.options.filter((o) => o.name === selectedRadio[i]))
.flat();

let estimationsMap = estimations.map((v) => v.options.filter((o) => o.name)).flat();
$: estimationsMapCheckbox = estimationsCheckbox
.map((v) => v.options.filter((v) => selectedCheckbox.includes(v.name)))
.flat();

$: combinedBudgetPower = estimationsMap
.filter((r) => selectedKeyValues.includes(r.name))
.reduce(
(a, b) => {
return {
lowBudget: a.lowBudget + Number(b['low-bud-point']),
highBudget: a.highBudget + Number(b['high-bud-point']),
lowPower: a.lowPower + Number(b['low-est-point']),
highPower: a.highPower + Number(b['high-est-point'])
};
},
{ lowBudget: 0, highBudget: 0, lowPower: 0, highPower: 0 }
);
$: combinedBudgetPower = [...estimationsMapRadio, ...estimationsMapCheckbox].reduce(
(a, b) => {
return {
lowBudget: a.lowBudget + Number(b['low-est-point']) * 14500,
highBudget: a.highBudget + Number(b['high-est-point']) * 14500,
lowPower: a.lowPower + Number(b['low-est-point']),
highPower: a.highPower + Number(b['high-est-point'])
};
},
{ lowBudget: 0, highBudget: 0, lowPower: 0, highPower: 0 }
);

// open / close form
$: Object.values(combinedBudgetPower).every((val) => val !== 0) ? (open = true) : (open = false);
$: open = Object.values(combinedBudgetPower).every((val) => val !== 0);
</script>

<section class="border-t mt-20" id="estimation">
Expand All @@ -58,92 +66,130 @@
</div>
</section>

<section class="container mx-auto px-container @container pt-20 pb-16 flex">
<section class="container mx-auto xl:px-container @container pt-20 pb-16 flex">
<div
class="rounded-lg border w-full overflow-hidden flex min-h-[unset] xl:min-h-[710px] flex-col xl:flex-row relative"
class="rounded-lg xl:border w-full xl:overflow-hidden flex min-h-[unset] flex-col xl:flex-row relative"
>
<img
class={clsx(
'absolute right-1/2 hidden xl:block transition-all ease-motion duration-500',
!open ? '-bottom-96 opacity-0 select-none' : 'bottom-0 opacity-100'
)}
src={$theme === 'dark' ? PreFooterAsset : PreFooterAssetLight}
width="354"
alt=""
/>
<div
class={clsx(
'flex transition-all duration-500 ease-motion bg-background-panel rounded-b-lg xl:rounded-r-lg',
open ? 'xl:w-1/2 w-full ring-1 ring-border' : 'w-full'
'flex transition-all duration-300 ease-motion w-full xl:bg-background-panel xl:rounded-b-lg xl:rounded-r-lg z-50',
open ? 'xl:ring-1 xl:ring-border xl:w-2/3' : 'w-full min-w-full'
)}
>
<div class="w-full @5xl:flex">
<div class="p-6 xl:p-8 flex flex-col justify-between h-full xl:max-w-2xl">
<div>
<div class="block w-full">
<div class="flex flex-col h-full relative z-10">
<div class="order-1 xl:pt-8 xl:px-8 px-container">
<h3 class="text-4xl max-w-md">
{block.title}
</h3>
<p class="my-4 text-xl text-foreground-secondary max-w-md">
{block.description}
</p>
<div class="py-4 xl:py-6 inline-flex lg:flex-row flex-col flex-wrap gap-4 relative">
<img
class={clsx(
'hidden xl:block absolute top-12 -right-4 translate-x-full',
open ? 'xl:hidden xl:opacity-0' : 'opacity-100'
)}
width="164"
src={srcArrow}
alt=""
/>
{#each estimations as { name, options }}
<MultiSelect
options={options.map((o) => o.name)}
bind:selected={selected[name]}
selectedLabel={name}
icon="plus"
class="w-full xs:w-fit"
/>
{/each}
</div>

<div class="flex flex-wrap gap-4 max-w-xl z-10 relative">
{#each selectedMap as option}
{#each option.value as op}
</div>
<div class="xl:order-2 order-3 xl:px-8 px-container">
<div
class={clsx('pb-4 pt-2 xl:pb-8 gap-4 relative w-full', open ? 'w-full' : 'xl:w-2/3')}
>
<div class="flex flex-col md:grid md:grid-cols-2 xl:grid-cols-3 gap-y-4 gap-x-5">
{#each estimations as options, i}
<div class="gap-2 grid">
<div class="grid grid-cols-2 items-center pt-4 pb-2">
<p class="leading-none text-foreground-secondary text-sm">
{options.name}
</p>
<button
class="leading-none text-foreground-tertiary text-sm hover:text-foreground-secondary transition-all flex justify-end"
on:click={() => {
selectedRadio[i] = '';
}}
>
{t('estimate.clear')}
</button>
</div>
<div class="grid grid-cols-2 xl:flex xl:flex-col gap-2">
{#each options.options as opt}
<label
for={`${options.name} / ${opt.name}`}
class={clsx(
'flex flex-col items-start py-2 px-3 transition-all hover:bg-foreground/2 cursor-pointer border rounded-sm hover:border-border-active hover:ring-2 text-sm/[20px] select-none'
)}
>
<div class="flex items-center justify-between w-full">
<p class="font-medium pr-3">{opt.name}</p>
<Radio
class="cursor-pointer shrink-0"
id={`${options.name} / ${opt.name}`}
value={opt.name}
name={options.name}
checked={selectedRadio[i] === opt.name}
bind:group={selectedRadio[i]}
/>
</div>
</label>
{/each}
</div>
</div>
{/each}
{#each estimationsCheckbox as options}
<div
class="flex items-center justify-between px-2.5 py-1.5 border border-border bg-background-offset text-xs font-semibold rounded-xs w-fit hover:transition-all focus-within:border-border-active focus-within:outline-none focus-within:ring-4 focus-within:ring-outline focus-within:transition-all hover:opacity-80"
class="gap-2 grid col-span-2 grid-cols-2 xl:gap-x-5 [&:last-child>p]:col-span-2"
>
{op.label}
<button
class="outline-none flex"
on:click={() => {
let opSelected = selected[option.key].filter(
(value) => value.label !== op.label
);
selected[option.key] = opSelected;
}}
>
<Icon class="ml-2" icon="close" />
</button>
<p class="pt-4 leading-none text-foreground-secondary text-sm grid pb-2">
{options.name}
</p>

{#each options.options as opt}
<label
for={`${options.name} / ${opt.name}`}
class={clsx(
'flex flex-col items-start py-2 px-3 transition-all hover:bg-foreground/2 cursor-pointer border rounded-sm hover:border-border-active hover:ring-2 text-sm/[20px] select-none'
)}
>
<div class="flex items-center justify-between w-full">
<p class="font-medium pr-3">{opt.name}</p>
<CheckboxGroup
bind:group={selectedCheckbox}
id={`${options.name} / ${opt.name}`}
value={opt.name}
name={options.name}
/>
</div>
</label>
{/each}
</div>
{/each}
{/each}
</div>
</div>
</div>

<div class="flex flex-col mt-5 xl:mt-3">
<p class="text-base font-medium text-foreground/60">{t('estimation.man.power')}</p>
<p class="text-xl font-medium">
{combinedBudgetPower.lowPower === 0
? '-'
: combinedBudgetPower.lowPower + ` to ` + combinedBudgetPower.highPower}
</p>
<p class="text-base font-medium text-foreground/60 mt-6">{t('estimation.total')}</p>
<p class="text-xl font-medium text-foreground-accent">
{combinedBudgetPower.lowBudget === 0
? '-'
: combinedBudgetPower.lowBudget + `€ to ` + combinedBudgetPower.highBudget + `€`}
</p>
<div
class="flex flex-col mt-2 xl:mt-3 mb-4 xl:mb-0 sticky top-0 xl:relative xl:top-auto order-2 xl:order-3 z-10 border-b xl:border-none w-screen xl:w-full bg-background xl:bg-transparent xl:pb-8 xl:px-8"
>
<div class="py-3 px-container xl:py-0 xl:px-0 flex xl:block gap-2 xl:gap-0">
<div class="w-1/2 xl:w-auto">
<p class="text-base/5 xl:text-base font-medium text-foreground/60">
{t('estimation.man.power')}
</p>
<p class="text-md md:text-xl font-medium">
{combinedBudgetPower.lowPower === 0
? '-'
: combinedBudgetPower.lowPower + ` to ` + combinedBudgetPower.highPower}
</p>
</div>
<div class="w-1/2 xl:w-auto">
<p class="text-base/5 xl:text-base font-medium text-foreground/60 mt-0 xl:mt-6">
{t('estimation.total')}
</p>
<p class="text-md xl:text-xl font-medium text-foreground-accent">
{combinedBudgetPower.lowBudget === 0
? '-'
: combinedBudgetPower.lowBudget +
`€ to ` +
combinedBudgetPower.highBudget +
`€`}
</p>
</div>
</div>
</div>
</div>
<div
Expand All @@ -152,11 +198,18 @@
open ? 'xl:hidden xl:opacity-0' : 'right-0 opacity-100'
)}
>
<img class="max-w-[710px]" {src} alt="" />
<img class="max-w-[710px] pointer-events-none" {src} alt="" />
</div>
</div>
</div>
<div class={clsx('xl:w-1/2 w-full p-8', open ? 'flex flex-col justify-between' : 'hidden')}>
<div
class={clsx(
'pt-8 xl:p-8 transition-all duration-300 w-full xl:w-auto z-10 px-container',
open
? 'flex flex-col justify-between flex-0.5 shrink-0 translate-y-0 xl:translate-x-0 xl:w-1/3 xl:translate-y-0'
: '-translate-y-full xl:translate-x-full xl:translate-y-0 hidden xl:flex'
)}
>
<div>
<h3 class="text-4xl max-w-[300px]">
{block.form_title}
Expand All @@ -167,7 +220,7 @@
</div>
<ContactForm variant="estimations">
<svelte:fragment slot="estimationsform">
<input name="estimations" hidden bind:value={selectedKeyValues} />
<input name="estimations" hidden bind:value={selectedValuesToNotion} />
</svelte:fragment>
</ContactForm>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/blocks/work-recognitions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

<div style="background-image: url({src});">
<section
class="container mx-auto px-container @container flex flex-col max-w-[1056px] pt-32 relative"
class="container mx-auto px-container @container flex flex-col max-w-[1056px] pt-16 xl:pt-32 relative"
>
<img src={EggChicken} class="absolute block drop-shadow-md -right-56 -bottom-80" alt="" />
<img src={QuimHead} class="absolute block drop-shadow-md -left-60 top-12" alt="" />
Expand Down
Loading
Loading