feat: redesign section progress, report, link, file, dan cancel pada project & division/task

- SectionProgress: progress bar animated, badge persentase, label status, task count
- SectionReport: header ikon, left accent border, TextExpandable dengan label Indonesia
- SectionLink: tap langsung buka URL, ikon per domain, long press untuk hapus
- SectionFile: icon container konsisten 30×30 di semua section
- SectionCancel: card subtle dengan warna error, konsisten dengan visual language baru
- TextExpandable: fix bug show/hide tidak muncul setelah content diupdate
- Tambah 14 style class baru di Styles.ts untuk menggantikan inline style
- Terapkan semua perubahan ke fitur division/task
- Fix menu "Edit Tugas" di sectionTanggalTugasTask yang terpotong karena overflow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 16:22:52 +08:00
parent eccfe29387
commit b61cd51628
16 changed files with 736 additions and 510 deletions

View File

@@ -37,6 +37,7 @@ export default function DetailProject() {
const { id } = useLocalSearchParams<{ id: string }>();
const [data, setData] = useState<Props>()
const [progress, setProgress] = useState(0)
const [taskStats, setTaskStats] = useState<{ done: number, total: number } | undefined>()
const [loading, setLoading] = useState(true)
const update = useSelector((state: any) => state.projectUpdate)
const [isMember, setIsMember] = useState(false)
@@ -60,6 +61,17 @@ export default function DetailProject() {
}
}
async function handleLoadTaskStats() {
try {
const hasil = await decryptToken(String(token?.current))
const response = await apiGetProjectOne({ user: hasil, cat: 'task', id: id })
const tasks: { status: number }[] = response.data
setTaskStats({ done: tasks.filter(t => t.status === 1).length, total: tasks.length })
} catch (error) {
console.error(error)
}
}
async function checkMember() {
try {
const hasil = await decryptToken(String(token?.current))
@@ -79,6 +91,10 @@ export default function DetailProject() {
handleLoad('progress')
}, [update.progress])
useEffect(() => {
handleLoadTaskStats()
}, [update.task])
useEffect(() => {
checkMember()
}, [])
@@ -88,6 +104,7 @@ export default function DetailProject() {
setRefreshing(true)
await handleLoad('data')
await handleLoad('progress')
await handleLoadTaskStats()
await new Promise(resolve => setTimeout(resolve, 2000));
setRefreshing(false)
};
@@ -126,7 +143,7 @@ export default function DetailProject() {
{
data?.reason != null && data?.reason != "" && <SectionCancel text={data?.reason} />
}
<SectionProgress text={`Kemajuan Kegiatan ${progress}%`} progress={progress} />
<SectionProgress progress={progress} doneCount={taskStats?.done} totalCount={taskStats?.total} />
<SectionReportProject refreshing={refreshing} />
<SectionTanggalTugasProject status={data?.status} member={isMember} refreshing={refreshing} />
<SectionFile status={data?.status} member={isMember} refreshing={refreshing} />