-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add promise detail page (#113)
* 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
Showing
5 changed files
with
315 additions
and
1 deletion.
There are no files selected for viewing
113 changes: 113 additions & 0 deletions
113
src/components/PromiseDetail/PromiseProgressTimeline.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)} /> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.