ciscoheat / sveltekit-superforms

Making SvelteKit forms a pleasure to use!
https://superforms.rocks
MIT License
2.14k stars 64 forks source link

If the form is invalid, my whole form gets cleared #27

Closed Bewinxed closed 1 year ago

Bewinxed commented 1 year ago

+page.server.ts

import type { ServerLoadEvent, Actions } from '@sveltejs/kit/types';
import type { PageServerLoad } from './$types';
import { fail } from '@sveltejs/kit';
import { superValidate } from 'sveltekit-superforms/server';
import { Listing } from 'src/types/listing';
import z from 'zod';
import { error, redirect } from '@sveltejs/kit';

const listingCrudSchema = Listing.extend({
    id: Listing.shape.id.optional()
});

const listingId = () => String(Math.random()).slice(2);

export const load = (async ({ url }) => {
    // READ listing
    // For simplicity, use the id query parameter instead of a route.
    const id = url.searchParams.get('id');
    // const listing = id ? listings.find((l) => l.id === id) : null;
    const listing = listings[0];
    console.log(listings);

    if (id && !listing) throw error(404, 'Listing not found');

    // If we're editing a listing, prefill the form with the listing data.
    const form = await superValidate(listing, listingCrudSchema);

    // Always return { form } in load and form actions.
    return { form };
}) satisfies PageServerLoad;

export const actions = {
    default: async (event) => {
        const form = await superValidate(event, listingCrudSchema);

        if (!form.valid) {
            console.log('form invalid');
            console.log('errors', form.errors);
            console.log('message', form.message);
            return fail(400, { form });
        }

        if (!form.data.id) {
            // CREATE user
            const listing = { ...form.data, id: listingId() };
            listings.push(listing);

            console.log('listing id', listing.id);
            throw redirect(303, '?id=' + listing.id);
        } else {
            // UPDATE user
            const listing = listings.find((u) => u.id == form.data.id);
            if (!listing) throw error(404, 'Listing not found.');

            listings[listings.indexOf(listing)] = { ...form.data, id: listing.id };

            form.message = 'Listing updated!';
            console.log(form.message);
            return { form };
        }
    }
} satisfies Actions;

// A simple listing "database"
const listings: z.infer<typeof Listing>[] = [];

When the form is invalid and submitted, the whole form gets cleared :( how to prevent this?

ciscoheat commented 1 year ago

Can you post the +page.svelte code as well? Nothing looks wrong here directly.

Bewinxed commented 1 year ago

Might be a bit long but here

<script lang="ts">
    import InputProgress from 'src/components/forms/InputProgress.svelte';
    import type { Snapshot } from './$types';
    import { toasts } from 'svelte-toasts';

    $: snapshotData = {
        form: $form,
        lastClickedSection: $lastClickedSection
    };

    function mergeObjectsRecursive(
        target: Record<string, unknown>,
        source: Record<string, unknown>
    ): Record<string, unknown> {
        for (const key in source) {
            if (
                typeof target[key] === 'object' &&
                target[key] !== null &&
                typeof source[key] === 'object' &&
                source[key] !== null
            ) {
                mergeObjectsRecursive(
                    target[key] as Record<string, unknown>,
                    source[key] as Record<string, unknown>
                );
            } else if (target[key] === undefined || target[key] === null || target[key] === '') {
                target[key] = source[key];
            }
        }
        return target;
    }

    export const snapshot: Snapshot = {
        capture: () => snapshotData,
        restore: (value) => {
            toasts.add({
                title: 'Restored',
                description: 'Your form has been restored from a previous session.',
                type: 'success',
                placement: 'top-right'
            });
            console.log(value);
            if (!$form.id) {
                $form = value.form;
            } else {
                $form = mergeObjectsRecursive($form, value.form);
            }
            $lastClickedSection = value.lastClickedSection;
        }
    };

    import {
        type ListingProject,
        PricingUnits,
        ProjectTypes,
        ListingPurposes
    } from 'src/types/listing';
    import { Accordion, AccordionItem } from '@skeletonlabs/skeleton';
    import Svelecte from 'svelecte';
    import {
        Avatar,
        InputChip,
        ProgressBar,
        ProgressRadial,
        RangeSlider
    } from '@skeletonlabs/skeleton';
    import Select from 'svelte-select';
    import type {
        CollectionNameMappingItem,
        CollectionSocialMetadata,
        CollectionStatsItem
    } from '../../types/helloMoon';
    let filterText = '';
    let value: any;
    let items: CollectionNameMappingItem[] = [];
    let searchable = true;
    let placeholder = '🔍 Type to find your collection...';

    // VALIDATION
    // $: {
    //  if ($form.purpose.includes('takeoverPartial')) {
    //      $form.partialTakeoverPercentage = undefined;
    //  }
    // }

    let loadOptions = async function (filterText: string) {
        return new Promise((resolve, reject) => {
            setTimeout(async () => {
                if (filterText.length < 3) {
                    return resolve([]);
                }
                const response = await fetch(`/api/project/search?collectionName=${filterText}`);
                if (!response.ok) {
                    console.log(
                        `Error fetching collection name mapping: ${response.status} ${response.statusText}`
                    );
                    return [];
                }

                const newItems = await response.json();
                return resolve(
                    newItems.map((item: CollectionNameMappingItem) => {
                        return {
                            label: item.collectionName,
                            value: item.helloMoonCollectionId
                        };
                    })
                );
            }, 100);
        });
    };

    import type { PageData } from './$types';
    import { page } from '$app/stores';
    import { superForm } from 'sveltekit-superforms/client';
    import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte';
    import { Listing, ListingPurpose } from 'src/types/listing';
    import { slide } from 'svelte/transition';
    import { Collection } from 'mongodb';
    import TechStackForm from 'src/components/TechStackForm.svelte';
    import SocialsForm from 'src/components/SocialsForm.svelte';
    import Icon from '@iconify/svelte';
    import AddressesForm from 'src/components/AddressesForm.svelte';
    import { writable } from 'svelte/store';

    export let data: PageData;
    // Client API:
    const { form, errors, enhance, delayed, message, constraints } = superForm(data.form, {
        scrollToError: 'smooth',
        autoFocusOnError: 'detect',
        errorSelector: '[data-invalid]'
    });

    function handleKeydown(event: KeyboardEvent) {
        if (event.key === 'Enter' && filterText.length > 0) {
            alert('ENTER PRESSED: ' + filterText);
        }
    }

    async function getSocialMetadata() {
        console.log('getting social metadata for');
        const response = await fetch(`/api/project/${$form.project.helloMoonCollectionId}/metadata`);
        if (!response.ok) {
            console.log(
                `Error fetching collection social metadata: ${response.status} ${response.statusText}`
            );
        }

        const metadata = (await response.json()) as CollectionSocialMetadata;

        // const metadata = {
        //  collectionName: 'DeGods',
        //  helloMoonCollectionId: '040de757c0d2b75dcee999ddd47689c4',
        //  narrative: 'A collection of 10,000 of the most degenerate gods in the universe.',
        //  external_url: 'https://degods.com/',
        //  symbol: null,
        //  description: null,
        //  image: 'https://metadata.degods.com/g/9999-dead.png',
        //  twitter: null,
        //  discord: null,
        //  website: null,
        //  categories: null,
        //  isUnverifiedCollection: false
        // };

        const updatedValues: ListingProject = {
            helloMoonCollectionId: metadata.helloMoonCollectionId,
            name: metadata.collectionName,
            website: metadata.external_url,
            description: metadata.narrative,
            image: metadata.image
        };

        // fill form data
        $form.project = { ...$form.project, ...updatedValues };
        if (metadata.twitter) {
            $form.project.socials.twitter = {
                network: 'twitter',
                url: metadata.twitter
            };
        }
        if (metadata.discord) {
            $form.project.socials.discord = {
                network: 'discord',
                url: metadata.discord
            };
        }

        return metadata;
    }

    async function getStats() {
        console.log('getting stats', $form.project);
        const response = await fetch(`/api/project/${$form.project.helloMoonCollectionId}/stats`);
        if (!response.ok) {
            console.log(`Error fetching collection stats: ${response.status} ${response.statusText}`);
        }

        const stats = (await response.json()) as CollectionStatsItem;

        const updatedValues = {
            floorPriceAtListing: parseInt(stats.floorPriceLamports) / 1000000000,
            launchDate: new Date(stats.startTime * 1000).toLocaleDateString('en-US'),
            launchPrice: parseInt(stats.mintPriceMode),
            ownersAtListing: parseInt(stats.currentOwnerCount),
            volumeAtListing: parseInt(stats.volume) / 1000000000,
            listingsAtListing: parseInt(stats.listingCount),
            supply: parseInt(stats.supply)
        };

        // fill form data
        $form.project = { ...$form.project, ...updatedValues };

        return stats;
    }

    const listingPurposes = {
        [ListingPurposes.enum.takeoverFull]: {
            label: 'Full Takeover',
            description: 'I want to sell my entire project.',
            icon: 'mdi:account-group'
        },
        [ListingPurposes.enum.takeoverPartial]: {
            label: 'Partial Takeover',
            description: 'I want to sell a portion of my project.',
            icon: 'mdi:account-group-outline'
        },
        [ListingPurposes.enum.funding]: {
            label: 'Funding',
            description: 'I want to raise funds for my project.',
            icon: 'mdi:cash-multiple'
        },
        [ListingPurposes.enum.others]: {
            label: 'Something Else',
            description: 'I want to list for another reason.',
            icon: 'mdi:help-circle-outline'
        }
    };

    // FORM PROGRESS
    // initialize the active index to -1, indicating no active accordion
    const lastClickedSection = writable(-1);
    let formProgress1 = 0;
    let formProgress2 = 0;
    let formProgress3 = 0;
</script>

<svelte:window on:keydown={handleKeydown} />

<!-- search bar -->
<div class="flex flex-col m-8 space-y-4">
    <h1>New Listing</h1>

    <!-- FORM START LETS GOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO -->

    <form class="flex flex-col gap-y-8" method="POST" use:enhance>
        <div class="flex flex-col border rounded-lg p-4 space-y-4 gap-y-4">
            <div id="accordion" class="space-y-12">
                <div
                    id="accordion-item"
                    class="accordion-item"
                    on:click={() => {
                        lastClickedSection.set(1);
                        console.log('clicked', $lastClickedSection);
                    }}
                    on:keydown={() => {
                        lastClickedSection.set(1);
                        console.log('clicked', $lastClickedSection);
                    }}
                >
                    <div class="flex flex-row mb-4">
                        <div class="w-full flex flex-row place-content-between">
                            <div class="flex flex-row place-items-center space-x-2">
                                <Icon icon="mdi:bulletin-board" />
                                <h5>Listing Details</h5>
                            </div>

                            <div>
                                <ProgressRadial
                                    class="h-12 w-12"
                                    value={formProgress1}
                                    font={150}
                                    stroke={100}
                                    meter="stroke-primary-500"
                                    track="stroke-primary-500/30">{formProgress1}%</ProgressRadial
                                >
                            </div>
                        </div>
                    </div>

                    <div class="border rounded-lg p-4 space-y-4">
                        <label class="label">
                            Reason for listing? <br /><br />
                            <div class="logo-cloud flex flex-row gap-0.5 border">
                                {#each Object.entries(ListingPurpose) as [purposeValue, purposeLabel]}
                                    <button
                                        class="logo-item p-2 text-left h-16 w-1/4 {$form.purpose.includes(purposeValue)
                                            ? '!bg-primary-600'
                                            : ''}"
                                        data-purpose={purposeValue}
                                        on:click|preventDefault={() => {
                                            if ($form.purpose.includes(purposeValue)) {
                                                $form.purpose = $form.purpose.filter((p) => p !== purposeValue);
                                            } else {
                                                $form.purpose.push(purposeValue);
                                                $form.purpose = [...new Set($form.purpose)];
                                            }
                                            if (!$form.purpose.includes('takeoverPartial')) {
                                                $form.partialTakeoverPercentage = undefined;
                                            }
                                        }}
                                    >
                                        <span> <Icon icon={listingPurposes[purposeValue].icon} /></span>
                                        <span> {listingPurposes[purposeValue].label}</span>
                                    </button>
                                {/each}
                            </div>

                            {#if $errors.purpose}<span class="invalid">{$errors.purpose}</span>{/if}
                        </label>
                        {#if $form.purpose.includes('takeoverPartial')}
                            <div transition:slide>
                                <RangeSlider
                                    class="range"
                                    name="partialTakeoverPercentage"
                                    data-invalid={$errors.partialTakeoverPercentage}
                                    bind:value={$form.partialTakeoverPercentage}
                                    type="range"
                                    max={100}
                                    ticked
                                >
                                    <div class="flex justify-between items-center">
                                        <div class="font-bold">Takeover %</div>
                                        <div class="badge variant-filled-primary text-white">
                                            {$form.partialTakeoverPercentage}
                                        </div>
                                    </div>
                                </RangeSlider>
                            </div>
                        {/if}

                        <div class="w-full grid grid-cols-1 md:grid-cols-2 gap-4">
                            <label class="space-y-1" for="project-search">
                                <span class="label">Asking Price</span>
                                <blockquote>Make sure you select the right currency</blockquote>
                                <div class="input-group input-group-divider grid-cols-[auto_1fr_auto]">
                                    <div class="input-group-shim"><i class="fa-solid fa-dollar-sign" />$</div>
                                    <InputProgress
                                        {errors}
                                        placeholder="Asking Price"
                                        bind:progress={formProgress1}
                                        bind:value={$form.price}
                                        progressAmount={2}
                                        name="price"
                                        type="number"
                                    />
                                    <select
                                        class="select"
                                        name="priceUnit"
                                        data-invalid={$errors.priceUnit}
                                        bind:value={$form.priceUnit}
                                    >
                                        {#each PricingUnits as priceUnit}
                                            <option value={priceUnit}>{priceUnit}</option>
                                        {/each}
                                        {#if $errors.priceUnit}<span class="invalid">{$errors.priceUnit}</span>{/if}
                                    </select>
                                </div>
                                {#if $errors.price}<span class="invalid">{$errors.price}</span>{/if}
                            </label>
                            <label class="space-y-1" for="project-search">
                                <span class="label">What does the price include?</span>
                                <!-- helper text -->
                                <blockquote>Select from below or add your own</blockquote>
                                <br />
                                <Svelecte
                                    options={['Project IP', 'Treasury', 'Source Code']}
                                    creatable={true}
                                    multiple={true}
                                    bind:value={$form.priceIncludes}
                                    placeholder="Type Multiple Items"
                                />
                            </label>
                            <label class="col-span-2"
                                >Reason for listing <br /><br />
                                <textarea
                                    class="textarea"
                                    name="reason"
                                    data-invalid={$errors.reason}
                                    bind:value={$form.reason}
                                />
                            </label>
                            <!-- listing headline -->
                            <label for="headline" class="col-span-2">
                                <span class="label">Listing Title <br /><br /></span>
                                <InputProgress
                                    {errors}
                                    placeholder="e.g. 'Looking to sell 50% of my project'"
                                    bind:progress={formProgress1}
                                    bind:value={$form.headline}
                                    progressAmount={5}
                                    name="headline"
                                    type="text"
                                />
                                {#if $errors.headline}<span class="invalid">{$errors.headline}</span>{/if}
                                <blockquote>{$form.headline}</blockquote>
                            </label>
                        </div>
                    </div>
                </div>

                <!-- ... -->
                <div
                    id="accordion-item"
                    on:click={() => {
                        lastClickedSection.set(2);
                        console.log('clicked', $lastClickedSection);
                    }}
                    on:keydown={() => {
                        lastClickedSection.set(2);
                        console.log('clicked', $lastClickedSection);
                    }}
                >
                    <div class="flex flex-row mb-4">
                        <div class="w-full flex flex-row place-content-between">
                            <div class="flex flex-row place-items-center space-x-2">
                                <Icon icon="ph:projector-screen-chart" />
                                <h5>Project Info</h5>
                            </div>

                            <div>
                                <ProgressRadial
                                    class="h-12 w-12"
                                    value={formProgress2}
                                    font={150}
                                    stroke={100}
                                    meter="stroke-primary-500"
                                    track="stroke-primary-500/30">{formProgress2}%</ProgressRadial
                                >
                            </div>
                        </div>
                    </div>
                    <div id="content">
                        <div class="border rounded-lg p-4 space-y-4">
                            <h4 class="font-semibold">Project Details</h4>
                            {#if $form.purpose}
                                <label>
                                    What kind of project are you listing? <br /><br />
                                    <select
                                        on:input={(e) => {
                                            $form.project.categories = Array.from(e.target.selectedOptions).map(
                                                (o) => o.value
                                            );
                                        }}
                                        class="select"
                                        name="project.categories"
                                        multiple
                                        size="3"
                                        data-invalid={$errors.project}
                                    >
                                        {#each ProjectTypes as projectType}
                                            <option value={projectType}>{projectType}</option>
                                        {/each}
                                    </select>
                                    {#if $errors.project}<span class="invalid">{$errors.purpose}</span>{/if}
                                </label>
                                <div class="space-y-4" transition:slide>
                                    <InputProgress
                                        {errors}
                                        name="helloMoonCollectionId"
                                        type="hidden"
                                        bind:value={$form.project.helloMoonCollectionId}
                                    />
                                    {#if $errors.project}<span class="invalid">{$errors.project}</span>{/if}
                                    {#if ($form.project?.categories ?? []).includes('NFT')}
                                        <label class="space-y-1" for="project-search">
                                            <h3 class="label">We can autofill your project's info</h3>
                                            <br />
                                            <Svelecte
                                                resetOnBlur={true}
                                                fetchResetOnBlur={true}
                                                valueAsObject
                                                minQuery={3}
                                                placeholder="🔍 Type to find your project"
                                                on:change={(e) => {
                                                    console.log('change', e.detail);
                                                    $form.project.collectionName = e.detail.label;
                                                    $form.project.helloMoonCollectionId = e.detail.value;
                                                    getSocialMetadata();
                                                    getStats();
                                                }}
                                                fetch={loadOptions}
                                            />
                                        </label>
                                        {#if $form.project.helloMoonCollectionId && !$form.project}
                                            {#await getSocialMetadata()}
                                                <div transition:slide class="p-4 space-y-2">
                                                    <ProgressBar label="Progress Bar" />
                                                    <p>Autofilling project data</p>
                                                </div>
                                            {:then}
                                                {#if $form.project}
                                                    <!-- post card -->
                                                    <div
                                                        class="flex bg-surface-500 shadow-lg rounded-lg md:mx-auto my-56 max-w-md md:max-w-2xl "
                                                    >
                                                        <!--horizantil margin is just for display-->
                                                        <div class="flex items-start px-4 py-6">
                                                            <Avatar
                                                                width="w-24"
                                                                src={$form.project.image}
                                                                class="rounded-full object-cover mr-4 shadow"
                                                            />
                                                            <div class="">
                                                                <div class="flex items-center justify-between">
                                                                    <h3 class="text-lg font-bold  -mt-1">
                                                                        {$form.project.collectionName}
                                                                    </h3>
                                                                    <small class="text-sm">{new Date().toLocaleDateString()}</small>
                                                                </div>
                                                                <p class="font-bold text-ellipsis">
                                                                    {$form.project.description}
                                                                </p>
                                                                {#await getStats()}
                                                                    <div transition:slide class="p-4 space-y-2">
                                                                        <ProgressBar label="Progress Bar" />
                                                                        <p>Loading latest stats...</p>
                                                                    </div>
                                                                {:then stats}
                                                                    <div class="mt-4 flex items-center">
                                                                        <div class="flex flex-col mr-2  text-sm">
                                                                            Supply
                                                                            <span>{parseInt(stats.supply)}</span>
                                                                        </div>
                                                                        <div class="flex flex-col mr-2 text-sm ">
                                                                            Floor Price
                                                                            <span>{parseInt(stats.floorPriceLamports) / 1000000000}</span>
                                                                        </div>
                                                                        <div class="flex mr-2 text-primary-100 text-sm">
                                                                            <svg
                                                                                fill="none"
                                                                                viewBox="0 0 24 24"
                                                                                class="w-4 h-4 mr-1"
                                                                                stroke="currentColor"
                                                                            >
                                                                                <path
                                                                                    stroke-linecap="round"
                                                                                    stroke-linejoin="round"
                                                                                    stroke-width="2"
                                                                                    d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"
                                                                                />
                                                                            </svg>
                                                                            <a
                                                                                href={$form.project.website}
                                                                                target="_blank"
                                                                                rel="noreferrer"><span>website </span></a
                                                                            >
                                                                        </div>
                                                                    </div>
                                                                {/await}
                                                            </div>
                                                        </div>
                                                    </div>
                                                {/if}
                                            {/await}
                                        {/if}
                                    {/if}
                                </div>
                            {/if}

                            <div class="w-full grid grid-cols-1 md:grid-cols-2 gap-4">
                                <hr class="col-span-2" />
                                <div class="flex flex-row space-x-2 col-span-2">
                                    <Icon icon="material-symbols:info-outline" />
                                    <h5>Project Info</h5>
                                </div>
                                <!-- replicate above -->
                                <InputProgress
                                    {errors}
                                    name="helloMoonCollectionId"
                                    type="hidden"
                                    bind:value={$form.project.helloMoonCollectionId}
                                    bind:progress={formProgress2}
                                    progressAmount={2}
                                    required
                                />

                                <label for="collectionName"
                                    >Project Name:

                                    <!-- replicate above -->
                                    <InputProgress
                                        {errors}
                                        name="collectionName"
                                        type="text"
                                        bind:value={$form.project.collectionName}
                                        bind:progress={formProgress2}
                                        progressAmount={2}
                                        required
                                    />
                                </label>
                                <label for="website"
                                    >Website:
                                    <!-- replicate above -->
                                    <InputProgress
                                        {errors}
                                        name="website"
                                        type="url"
                                        bind:value={$form.project.website}
                                        bind:progress={formProgress2}
                                        progressAmount={2}
                                        required
                                    />
                                </label>
                                <label class="label"
                                    >Launch Date:
                                    <input
                                        name="launchDate"
                                        data-invalid={$errors?.project}
                                        class="input"
                                        type="date"
                                        placeholder="Enter the project's launch date"
                                        bind:value={$form.project.launchDate}
                                        required
                                    />
                                </label>
                                <label for="description" class="col-span-2"
                                    >Description:
                                    <textarea
                                        name="description"
                                        data-invalid={$errors?.project}
                                        class="textarea"
                                        placeholder="Enter the project's description"
                                        bind:value={$form.project.description}
                                        required
                                    />
                                </label>
                            </div>
                            <div class="col-span-2 space-y-4 w-full grid grid-cols-1 md:grid-cols-2 gap-4">
                                <hr class="col-span-2" />
                                <div class="flex flex-row space-x-2 col-span-2">
                                    <Icon icon="fluent-mdl2:financial" />
                                    <h6>Project Financials</h6>
                                </div>
                                <label for="floorPriceAtListing"
                                    >Floor Price at Listing:
                                    <input
                                        name="floorPriceAtListing"
                                        data-invalid={$errors?.project}
                                        class="input"
                                        type="number"
                                        readonly
                                        placeholder="Enter the floor price at listing"
                                        bind:value={$form.project.floorPriceAtListing}
                                        min="0"
                                    />
                                </label>

                                <label for="supply"
                                    >Supply:
                                    <input
                                        name="supply"
                                        data-invalid={$errors?.project}
                                        class="input"
                                        type="number"
                                        readonly
                                        placeholder="Enter the supply"
                                        bind:value={$form.project.supply}
                                        min="1"
                                        required
                                    />
                                </label>
                                <label for="supply"
                                    >Current Holders:
                                    <input
                                        name="supply"
                                        data-invalid={$errors?.project}
                                        class="input"
                                        type="number"
                                        readonly
                                        placeholder="Enter the supply"
                                        bind:value={$form.project.ownersAtListing}
                                        min="1"
                                        required
                                    />
                                </label>
                                <label for="volume">
                                    Volume:
                                    <input
                                        name="volume"
                                        data-invalid={$errors?.project}
                                        class="input"
                                        type="number"
                                        readonly
                                        placeholder="Enter the volume"
                                        bind:value={$form.project.volumeAtListing}
                                        min="0"
                                    />
                                </label>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <div
                id="accordion-item"
                class="accordion-item border rounded-lg p-4 space-y-4"
                on:click={() => {
                    lastClickedSection.set(3);
                    console.log('clicked', $lastClickedSection);
                }}
                on:keydown={() => {
                    lastClickedSection.set(3);
                    console.log('clicked', $lastClickedSection);
                }}
            >
                <div class="flex flex-row mb-4">
                    <div class="w-full flex flex-row place-content-between">
                        <div class="flex flex-row place-items-center space-x-2">
                            <Icon icon="iconoir:profile-circle" />
                            <h5>Project Links</h5>
                        </div>

                        <div>
                            <ProgressRadial
                                class="h-12 w-12"
                                value={formProgress3}
                                font={150}
                                stroke={100}
                                meter="stroke-primary-500"
                                track="stroke-primary-500/30">{formProgress3}%</ProgressRadial
                            >
                        </div>
                    </div>
                </div>
                <div id="content">
                    <div class="col-span-2">
                        <div class="flex flex-row space-x-2">
                            <h6>Social Links</h6>
                        </div>

                        <blockquote>Add the social links of your project</blockquote>
                    </div>
                    <div class="col-span-2 transition-all duration-700">
                        <SocialsForm {form} />
                    </div>
                    <hr class="col-span-2" />
                    <div class="col-span-2">
                        <div class="flex flex-row space-x-2">
                            <Icon icon="ph:stack" />
                            <h6>Tech Stack</h6>
                        </div>
                        <blockquote>Add the technologies used in your website/app/etc...</blockquote>
                    </div>
                    <div class="col-span-2 transition-all duration-700">
                        <TechStackForm {form} />
                    </div>
                    <hr class="col-span-2" />
                    <div class="col-span-2">
                        <div class="flex flex-row space-x-2">
                            <Icon icon="carbon:wallet" />
                            <h6>Project Addresses</h6>
                        </div>
                        <blockquote>Add your royalty/treasury/other important addresses</blockquote>
                    </div>
                    <div class="col-span-2 transition-all duration-700">
                        <AddressesForm {form} />
                    </div>
                    <hr class="col-span-2" />
                </div>
            </div>
        </div>

        <button type="submit" class="btn bg-green-600">Submit</button>
        {#if $delayed}Working...{/if}
        {#if $message}
            <h3 class:invalid={$page.status >= 400}>{$message}</h3>
        {/if}
    </form>
    <SuperDebug data={$form} />
    <!-- show result card -->
</div>

<style>
    :global(.svelecte-control) {
        --sv-bg: rgb(var(--color-surface-700)) !important;
        --sv-color: white !important;
        --sv-border: 1px solid rgb(var(--color-surface-500)) !important;
        --sv-item-selected-bg: rgb(var(--color-surface-700)) !important;
        --sv-item-active-bg: rgb(var(--color-surface-700)) !important;
        --sv-item-btn-bg: rgb(var(--color-surface-700)) !important;
        --sv-item-btn-bg-hover: rgb(var(--color-surface-700)) !important;
        --sv-item-highlight-color: rgb(var(--color-surface-700)) !important;
        --sv-item-color: white !important;
        --sv-item-selected-color: white !important;
        --sv-item-border: 1px solid rgb(var(--color-surface-500)) !important;
        --sv-color: rgb(var(--text-primary-100)) !important;
        --sv-placeholder-color: white !important;
        --sv-highlight-bg: rgb(var(--color-surface-100)) !important;
        --sv-highlight-color: rgb(var(--color-surface-700)) !important;
        /* --sv-active-border: 1px solid red !important;
        --sv-border-color: red !important;
        --sv-disabled-border-color: red !important;
        --sv-item-color: black !important; */
        --sv-item-active-color: red !important;
        /* --sv-item-active-bg: red !important; */
    }
    .invalid {
        color: red;
    }
</style>
ciscoheat commented 1 year ago

Quite long, yes. :) I see nothing obviously wrong with how you use the library, could it be something with the snapshot code, that sets $form, and may do that after a post?

Bewinxed commented 1 year ago

I'll try removing it and seeing what happens then get back to you, in a few hours

On Sun, 12 Mar 2023 at 7:31 PM Andreas Söderlund @.***> wrote:

Quite long, yes. :) I see nothing obviously wrong with how you use the library, could it be something with the snapshot code, that sets $form, and may do that after a post?

— Reply to this email directly, view it on GitHub https://github.com/ciscoheat/sveltekit-superforms/issues/27#issuecomment-1465242241, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACFY5BOGDMYWYQRYNJVDGZLW3X27TANCNFSM6AAAAAAVYGLQKI . You are receiving this because you authored the thread.Message ID: @.***>

ciscoheat commented 1 year ago

Ok, good luck. You could try clearing out most things except SuperDebug and see if you get the form data back, or if it goes missing during the server roundtrip?

Bewinxed commented 1 year ago

Apparently when the form is invalid the invalid fields are cleared, and since some of the invalid fields are inside the nested object, the whole nested object gets wiped :(

edit: forgot to remove this ``` // fill form data $form.project = { ...$form.project, ...updatedValues };

removed the line at the end and now it doesn't get cleared!

I passed the nested project as another form so that i can use the constraints from there

form invalid errors { user: [ 'String must contain at least 1 character(s)' ], financials: [ 'Required', 'Required' ], project: [ 'Required', 'Required', 'Required', 'Required', 'Required', 'Required', 'Required', 'Required', 'Required', 'Required', 'Required', 'Required', 'Required' ], overview: [ 'String must contain at least 1 character(s)' ] }

ciscoheat commented 1 year ago

@Bewinxed Check out the announcement for v0.6, it has an example of how to use the new nested data feature: https://github.com/ciscoheat/sveltekit-superforms/discussions/41

ciscoheat commented 1 year ago

@Bewinxed did you figure this out, so the issue can be closed?

Bewinxed commented 1 year ago

Yes thanks a lot!

On Thu, 13 Apr 2023 at 9:24 PM Andreas Söderlund @.***> wrote:

@Bewinxed https://github.com/Bewinxed did you figure this out, so the issue can be closed?

— Reply to this email directly, view it on GitHub https://github.com/ciscoheat/sveltekit-superforms/issues/27#issuecomment-1507431302, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACFY5BNUTYXGUHHO7W6QFI3XBBAENANCNFSM6AAAAAAVYGLQKI . You are receiving this because you were mentioned.Message ID: @.***>