|
10 | 10 | const { data }: PageProps = $props();
|
11 | 11 | const client = createClient({ fetch });
|
12 | 12 |
|
13 |
| - const project = data.project; |
14 | 13 | // TODO: ローディング中の UI を追加
|
15 | 14 | let participantName = $state<string>(data.prev?.name ?? "");
|
16 | 15 | let rolesCount = $state<number>(data.prev?.roles_count ?? 1);
|
|
20 | 19 | return { role, score };
|
21 | 20 | }),
|
22 | 21 | );
|
23 |
| - console.log(data.roles); |
24 | 22 |
|
25 | 23 | async function postPreference() {
|
26 | 24 | formState = "submitting";
|
27 |
| -
|
28 |
| - const preference = safeParse(PreferenceSchema, { |
29 |
| - browserId: null, |
30 |
| - participantName: participantName, |
31 |
| - rolesCount: data.project.multiple_roles === 1 ? rolesCount : null, |
32 |
| - ratings: ratings.map((rating) => ({ |
33 |
| - roleId: rating.role.id, |
34 |
| - score: rating.score, |
35 |
| - })), |
36 |
| - }); |
37 |
| - // TODO: handle it better |
38 |
| - if (!preference.success) throw new Error("failed to validate preference"); |
39 |
| -
|
40 |
| - if (data.prev) { |
41 |
| - // PUT |
42 |
| - const res = await client.projects[":projectId"].preferences.$put({ |
43 |
| - json: preference.output, |
44 |
| - param: { projectId: project.id }, |
| 25 | + try { |
| 26 | + const projectId = data.project.id; |
| 27 | + const preference = safeParse(PreferenceSchema, { |
| 28 | + participantName, |
| 29 | + rolesCount: data.project.multiple_roles === 1 ? rolesCount : null, |
| 30 | + ratings: ratings.map((rating) => ({ |
| 31 | + roleId: rating.role.id, |
| 32 | + score: rating.score, |
| 33 | + })), |
45 | 34 | });
|
46 |
| - if (!res.ok) |
47 |
| - throw new Error( |
48 |
| - `Failed to submit: got ${res.status} with text ${await res.text()}`, |
49 |
| - ); |
50 |
| - } else { |
51 |
| - // POST |
52 |
| - const res = await client.projects[":projectId"].preferences.$post({ |
53 |
| - json: preference.output, |
54 |
| - param: { projectId: project.id }, |
55 |
| - }); |
56 |
| - if (!res.ok) |
57 |
| - throw new Error( |
58 |
| - `Failed to submit: got ${res.status} with text ${await res.json()}`, |
59 |
| - ); |
| 35 | + // TODO: handle it better |
| 36 | + if (!preference.success) throw new Error("failed to validate preference"); |
| 37 | +
|
| 38 | + if (data.prev) { |
| 39 | + // PUT |
| 40 | + const res = await client.projects[":projectId"].preferences.$put({ |
| 41 | + json: preference.output, |
| 42 | + param: { projectId }, |
| 43 | + }); |
| 44 | + if (!res.ok) |
| 45 | + throw new Error( |
| 46 | + `Failed to submit: got ${res.status} with text ${await res.text()}`, |
| 47 | + ); |
| 48 | + } else { |
| 49 | + // POST |
| 50 | + const res = await client.projects[":projectId"].preferences.$post({ |
| 51 | + json: preference.output, |
| 52 | + param: { projectId }, |
| 53 | + }); |
| 54 | + if (!res.ok) |
| 55 | + throw new Error( |
| 56 | + `Failed to submit: got ${res.status} with text ${await res.json()}`, |
| 57 | + ); |
| 58 | + } |
| 59 | + goto("/done"); |
| 60 | + formState = "done"; |
| 61 | + } catch (err) { |
| 62 | + console.error(err); |
| 63 | + formState = "error"; |
| 64 | + setTimeout(() => { |
| 65 | + formState = "ready"; |
| 66 | + }, 1000); |
60 | 67 | }
|
61 |
| - goto("/done"); |
62 |
| - formState = "done"; |
63 | 68 | }
|
64 | 69 |
|
65 | 70 | let formState = $state<"ready" | "submitting" | "error" | "done">("ready");
|
66 | 71 | const closed = $derived.by(() => {
|
67 | 72 | if (data.project.closed_at === null) return false;
|
68 | 73 | return new Date(data.project.closed_at).getTime() < Date.now();
|
69 | 74 | });
|
| 75 | + const maxRoles = $derived(data.roles.length); |
| 76 | +
|
70 | 77 | const formVerb = $derived(data.prev ? "更新" : "送信");
|
71 | 78 |
|
72 | 79 | const resultLink = $derived(
|
73 | 80 | generateURL({
|
74 |
| - pathname: `${project.id}/result`, |
| 81 | + pathname: `${data.project.id}/result`, |
75 | 82 | }).href,
|
76 | 83 | );
|
77 | 84 | </script>
|
|
83 | 90 | <a class="btn btn-primary" href={resultLink}> 結果を見る </a>
|
84 | 91 | </div>
|
85 | 92 | {/if}
|
86 |
| - {#if project === null} |
| 93 | + {#if data.project === null} |
87 | 94 | <div class="hm-blocks-container">
|
88 | 95 | <p>プロジェクトが見つかりませんでした</p>
|
89 | 96 | </div>
|
90 | 97 | {:else}
|
| 98 | + {@const p = data.project} |
91 | 99 | <form
|
92 | 100 | method="POST"
|
93 | 101 | onsubmit={async (e) => {
|
|
97 | 105 | >
|
98 | 106 | <div class="hm-blocks-container">
|
99 | 107 | <div class="hm-block">
|
100 |
| - <h2 class="text-xl">{project.name}</h2> |
101 |
| - {#if project.description} |
102 |
| - <p class="text-sm">{project.description}</p> |
| 108 | + <h2 class="text-xl">{p.name}</h2> |
| 109 | + {#if p.description} |
| 110 | + <p class="text-sm">{p.description}</p> |
103 | 111 | {/if}
|
104 | 112 | </div>
|
105 | 113 | <div class="hm-block">
|
|
114 | 122 | disabled={closed}
|
115 | 123 | />
|
116 | 124 | </div>
|
117 |
| - {#if project.multiple_roles == 1} |
| 125 | + {#if p.multiple_roles == 1} |
118 | 126 | <div class="hm-block">
|
119 | 127 | <h2 class="text-xl">配属される役職数の希望</h2>
|
120 | 128 | <input
|
121 | 129 | type="number"
|
122 |
| - class="input bg-white text-base" |
123 |
| - placeholder="回答を入力" |
| 130 | + class="input validator bg-white text-base" |
124 | 131 | bind:value={rolesCount}
|
| 132 | + max={data.roles.length} |
125 | 133 | disabled={closed}
|
126 | 134 | />
|
| 135 | + <div class="w-full max-w-xs"> |
| 136 | + <input |
| 137 | + bind:value={rolesCount} |
| 138 | + class="range range-primary" |
| 139 | + type="range" |
| 140 | + min="1" |
| 141 | + max={maxRoles} |
| 142 | + step="1" |
| 143 | + /> |
| 144 | + <div class="flex justify-between px-2.5 mt-2 text-xs"> |
| 145 | + {#each { length: maxRoles } as _} |
| 146 | + <span class="select-none">|</span> |
| 147 | + {/each} |
| 148 | + </div> |
| 149 | + <div class="flex justify-between px-2.5 mt-2 text-xs"> |
| 150 | + {#each Array.from( { length: maxRoles }, ).map((_, i) => i + 1) as val} |
| 151 | + <span class="select-none">{val}</span> |
| 152 | + {/each} |
| 153 | + </div> |
| 154 | + </div> |
127 | 155 | </div>
|
128 | 156 | {/if}
|
129 | 157 | <RolesSelector bind:ratings {closed} />
|
130 | 158 | <div class="flex justify-end">
|
131 |
| - {#if closed} |
132 |
| - <button type="submit" class="btn btn-primary" disabled> |
| 159 | + <button |
| 160 | + type="submit" |
| 161 | + class="btn btn-primary" |
| 162 | + disabled={closed || formState !== "ready"} |
| 163 | + > |
| 164 | + {#if closed} |
133 | 165 | 既に締め切られています
|
134 |
| - </button> |
135 |
| - {:else if formState === "ready"} |
136 |
| - <button type="submit" class="btn btn-primary"> |
| 166 | + {:else if formState === "ready"} |
137 | 167 | {formVerb}
|
138 |
| - </button> |
139 |
| - {:else if formState === "submitting"} |
140 |
| - <button type="submit" class="btn btn-primary" disabled> |
| 168 | + {:else if formState === "submitting"} |
141 | 169 | <span class="loading loading-spinner"></span>
|
142 | 170 | {formVerb}中...
|
143 |
| - </button> |
144 |
| - {:else if formState === "error"} |
145 |
| - <button type="submit" class="btn btn-primary" disabled> |
146 |
| - <span class="loading loading-spinner"></span> |
| 171 | + {:else if formState === "error"} |
147 | 172 | {formVerb}に失敗しました
|
148 |
| - </button> |
149 |
| - {:else if formState === "done"} |
150 |
| - <button type="submit" class="btn btn-primary" disabled> |
| 173 | + {:else if formState === "done"} |
151 | 174 | 完了
|
152 |
| - </button> |
153 |
| - {/if} |
| 175 | + {/if} |
| 176 | + </button> |
154 | 177 | </div>
|
155 | 178 | </div>
|
156 | 179 | </form>
|
|
0 commit comments