Skip to content

Commit

Permalink
feat: add promise detail page (#113)
Browse files Browse the repository at this point in the history
* feat: update ui and add some components

* feat: add progress timeline component

* feat: add data period mark

* feat: update mobile layout

* feat: adjust gap

* feat: remove ref button

* feat: add form link and promise title

* feat: add promise status description

* fix: refactor

* feat: remove promise clarification blank box

---------

Co-authored-by: Th1nkK1D <[email protected]>
  • Loading branch information
kjametn and Th1nkK1D authored Oct 9, 2024
1 parent c9558e1 commit a1eedf5
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 1 deletion.
113 changes: 113 additions & 0 deletions src/components/PromiseDetail/PromiseProgressTimeline.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<script lang="ts">
import VoteCard from '$components/VoteCard/VoteCard.svelte';
import { formatThaiDate } from '$lib/date-parser';
import { PromiseStatus, type Promise } from '$models/promise';
import { Bullhorn, CheckmarkFilled } from 'carbon-icons-svelte';
import { twMerge } from 'tailwind-merge';
export let promise: Promise;
$: timeline = promise.progresses.sort((a, b) => {
return b.date > a.date ? 1 : -1;
});
const sumObj = (obj: Record<string, number>) => {
return Object.values(obj).reduce((acc, value) => acc + value, 0);
};
const highlightedVoteByGroups = (resultsByAffiliation: any[], key: string) => {
return resultsByAffiliation.map((res) => {
return {
name: res.name,
count: res.resultSummary[key],
total: sumObj(res.resultSummary)
};
});
};
</script>

{#each timeline as progress, index}
<div
class={twMerge(
'p-4 pb-0 text-text-01',
progress.type === 'indirect' && 'text-gray-60',
index === 0 && promise.status === PromiseStatus.inProgress && 'bg-yellow-10',
index === 0 && promise.status === PromiseStatus.fulfilled && 'bg-green-10',
index === 0 && promise.status === PromiseStatus.unhonored && 'bg-magenta-10'
)}
>
{#if index === 0 && promise.status !== PromiseStatus.notStarted}
<div class="heading-01 mb-2 text-text-primary">ความเคลื่อนไหวล่าสุด</div>
{/if}
<div class="flex gap-4">
<div class="relative flex flex-col items-center">
<div
class={twMerge(
'absolute inset-0 mx-auto w-[1px] flex-1 border-l border-l-text-primary',
index !== 0 && '-top-4',
progress.type === 'indirect' && 'border-dashed',
index === timeline.length - 1 && 'h-4',
progress.type === 'indirect' && 'border-l-gray-60'
)}
></div>
{#if progress.type === 'checkpoint'}
<CheckmarkFilled
size={24}
class={twMerge(
'relative bg-white',
index === 0 && promise.status === PromiseStatus.inProgress && 'bg-yellow-10',
index === 0 && promise.status === PromiseStatus.fulfilled && 'bg-green-10',
index === 0 && promise.status === PromiseStatus.unhonored && 'bg-magenta-10'
)}
/>
{/if}
{#if progress.type === 'indirect'}
<Bullhorn
size={24}
class={twMerge(
'relative bg-white text-gray-60',
index === 0 && promise.status === PromiseStatus.inProgress && 'bg-yellow-10',
index === 0 && promise.status === PromiseStatus.fulfilled && 'bg-green-10',
index === 0 && promise.status === PromiseStatus.unhonored && 'bg-magenta-10'
)}
/>
{/if}
</div>
<div class="flex flex-1 flex-col gap-6 md:flex-row">
<div class="mb-4 flex flex-1 flex-col gap-2">
<div class="body-01">{formatThaiDate(progress.date, true)}</div>
<div class="heading-02">{progress.title}</div>
{#if progress.description}
<div class="body-01">{progress.description}</div>
{/if}
{#if progress.reference}
<div class="label-01 text-gray-60">
ที่มา: <a
href={progress.reference.url}
target="_blank"
rel="noopener noreferrer"
class="underline">{progress.reference.label}</a
>
{progress.reference.description}
</div>
{/if}
</div>
{#if progress.votingSummary}
<VoteCard
voting={{
id: progress.votingSummary.id,
nickname: progress.votingSummary.title || '',
date: progress.votingSummary.date,
result: progress.votingSummary.result
}}
highlightedVoteByGroups={highlightedVoteByGroups(
progress.votingSummary.resultsByAffiliation,
progress.votingSummary.result
)}
class="mb-4"
/>
{/if}
</div>
</div>
</div>
{/each}
45 changes: 45 additions & 0 deletions src/components/PromiseDetail/PromiseStatusModal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script lang="ts">
import { Modal } from 'carbon-components-svelte';
import PromiseStatusTag from './PromiseStatusTag.svelte';
import type { PromiseStatus } from '$models/promise';
export let open = false;
export let onClose: () => void;
const statusList = [
{
label: 'รอคำชี้แจงเพิ่มเติม',
text: 'เราพบว่าคำสัญญานี้มีความคลุมเครือและกำลังอยู่ในระหว่างการขอคำชี้แจงเพิ่มเติม'
},
{
label: 'ไม่พบความเคลื่อนไหว',
text: 'เราไม่พบข้อมูลความเคลื่อนไหวที่เกี่ยวกับคำสัญญานี้'
},
{
label: 'กำลังดำเนินการ',
text: 'เราพบข้อมูลความคืบหน้า แต่ยังไม่บรรลุเป้าหมายที่ได้สัญญาไว้'
},
{
label: 'ดำเนินการแล้ว',
text: 'เราพบข้อมูลความคืบหน้า และข้อมูลที่ชี้ว่าได้บรรลุเป้าหมายที่ได้สัญญาไว้แล้ว'
},
{
label: 'เลิกดำเนินการ',
text: 'เราพบข้อมูลความคืบหน้า ที่ชี้ว่ามีการเลิกดำเนินการเพื่อบรรลุเป้าหมายตามคำสัญญานี้แล้ว'
}
] as { label: PromiseStatus; text: string }[];
</script>

<Modal {open} passiveModal hasScrollingContent size="xs" on:close={onClose}>
<div slot="heading">
<div class="heading-03">สถานะคำสัญญา</div>
</div>
<div class="mt-4 flex flex-col gap-4">
{#each statusList as status}
<div>
<PromiseStatusTag status={status.label} />
<div class="body-02 mt-2">{status.text}</div>
</div>
{/each}
</div>
</Modal>
18 changes: 18 additions & 0 deletions src/components/PromiseDetail/PromiseStatusTag.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts">
import { PromiseStatus } from '$models/promise';
import { twMerge } from 'tailwind-merge';
export let status: PromiseStatus;
</script>

<div
class={twMerge(
'heading-02 w-fit flex-none rounded-3xl bg-gray-30 px-2 py-1',
[PromiseStatus.clarifying, PromiseStatus.notStarted].includes(status) && 'bg-gray-30',
status === PromiseStatus.inProgress && 'bg-yellow-20',
status === PromiseStatus.fulfilled && 'bg-green-50 text-white',
status === PromiseStatus.unhonored && 'bg-magenta-50 text-white'
)}
>
{status}
</div>
137 changes: 136 additions & 1 deletion src/routes/promises/[id]/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,139 @@
<script lang="ts">
import DataPeriodRemark from '$components/DataPeriodRemark.svelte';
import PromiseProgressTimeline from '$components/PromiseDetail/PromiseProgressTimeline.svelte';
import PromiseStatusModal from '$components/PromiseDetail/PromiseStatusModal.svelte';
import PromiseStatusTag from '$components/PromiseDetail/PromiseStatusTag.svelte';
import Share from '$components/Share/Share.svelte';
import { Breadcrumb, BreadcrumbItem, Button } from 'carbon-components-svelte';
import { SendAlt } from 'carbon-icons-svelte';
export let data;
console.log(JSON.stringify(data, null, 2));
$: ({ promise } = data);
console.log('promise?.statements?.length:', promise?.statements?.length);
let showStatusListModal = false;
const TITLE_MAX_LENGTH = 45;
$: pageTitle =
promise?.statements?.[0]?.length > TITLE_MAX_LENGTH
? promise.statements[0].slice(0, TITLE_MAX_LENGTH) + '...'
: promise.statements?.[0];
</script>

<Breadcrumb
noTrailingSlash
class="body-compact-01 px-4 py-2 [&>.bx--breadcrumb]:flex [&>.bx--breadcrumb]:flex-wrap"
>
<BreadcrumbItem href="/">หน้าหลัก</BreadcrumbItem>
<BreadcrumbItem href="/promises">ติดตามคำสัญญา</BreadcrumbItem>
<BreadcrumbItem>{pageTitle}</BreadcrumbItem>
</Breadcrumb>
{#if promise.coverImageUrl}
<img class="max-h-[300px] w-full object-cover" src={promise.coverImageUrl} alt="coverImageUrl" />
{/if}
<div class="px-4 py-8 md:px-16 md:py-12">
<div class="heading-01 text-text-03">คำสัญญาของพรรคการเมือง</div>
<div class="mt-1 flex items-center gap-2">
<img
src={promise.party.logo}
alt=""
class="h-8 w-8 rounded-full border border-gray-30 object-cover"
/>
<div class="body-01">พรรค{promise.party.name}</div>
</div>
<div class="mt-4 flex flex-nowrap gap-3 overflow-x-auto overflow-y-hidden">
{#each promise.statements as statement}
<div class="flex min-w-[80%] gap-3">
<div class="flex flex-none flex-col items-center gap-2">
<img src="/images/promises/quote.svg" alt="" class="w-6" />
<div class="w-1 flex-1 bg-ui-03"></div>
</div>
<div class="heading-03 text-text-primary">
{statement}
</div>
</div>
{/each}
</div>
<div class="mt-4 flex flex-col gap-2 xl:flex-row xl:items-center">
<PromiseStatusTag status={promise.status} />
<div class="body-01 text-text-01">
[A definition that helps viewers understand the meaning and criteria of the status]
</div>
<button
class="helper-text-01 w-fit flex-none text-link-01 underline"
on:click={() => (showStatusListModal = true)}
>
ดูความหมายสถานะอื่นๆ
</button>
</div>
<div class="mt-8 flex flex-col justify-between gap-8 xl:flex-row">
<div class="flex flex-col gap-2">
<div class="flex flex-wrap gap-1">
<div class="heading-01 mt-1">คีย์เวิร์ด</div>
{#each promise.keywords as keyword}
<span class="label-01 rounded-3xl bg-gray-10 px-2 py-1">{keyword}</span>
{/each}
</div>
<div class="flex flex-wrap gap-1">
<div class="heading-01 mt-1">หมวด</div>
{#each promise.categories as category}
<span class="label-01 rounded-3xl border px-2 py-1">{category}</span>
{/each}
</div>
<div class="flex flex-col gap-1 md:flex-row">
<div class="heading-01">ที่มา</div>
<div>
{#each promise.references as reference}
<div class="leading-4">
<a href={reference.url} target="_blank" class="helper-text-01 text-link-01 underline">
{reference.label}
</a>
<span class="label-01 text-gray-60">{reference.description}</span>
</div>
{/each}
</div>
</div>
</div>
<div class="flex max-w-[224px] flex-col items-start gap-3 md:mt-0">
<DataPeriodRemark />
<Share label="แชร์คำสัญญา" />
</div>
</div>
</div>
<div class="px-4 py-8 md:px-16 md:py-12">
<div class="fluid-heading-04">ความคืบหน้าที่เกี่ยวข้อง</div>
<hr class="mt-4 border-gray-20" />
<div class="mb-4 mt-3 flex justify-between">
<div class="heading-02 flex flex-col gap-1 md:flex-row md:items-center">
สถานะ <PromiseStatusTag status={promise.status} />
</div>
<button
class="helper-text-01 h-fit text-link-01 underline"
on:click={() => (showStatusListModal = true)}
>
คำสัญญามีสถานะอะไรบ้าง?
</button>
</div>
<PromiseProgressTimeline {promise} />
<div class="mt-8 flex flex-col justify-between gap-4 bg-gray-10 p-6 md:flex-row">
<div class="text-01">
<div class="heading-02">
พบความเคลื่อนไหวที่อัพเดตกว่านี้ หรือ มีข้อทักท้วงการจัดสถานะของนโยบายนี้?
</div>
<div class="body-01 mt-1">
ทีมงานยินดีรับฟังเพื่อนำไปปรับปรุงข้อมูลในเว็บไซต์ให้สมบูรณ์และสมเหตุสมผลที่สุด
</div>
</div>
<Button
target="_blank"
href="https://forms.gle/feikqf5TFDqjVKPx6"
icon={SendAlt}
kind="secondary"
>
แจ้ง/ทักท้วงข้อมูล
</Button>
</div>
</div>
<PromiseStatusModal open={showStatusListModal} onClose={() => (showStatusListModal = false)} />
3 changes: 3 additions & 0 deletions static/images/promises/quote.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a1eedf5

Please sign in to comment.