Fix QC Kak Inno Tgl 4 & 5 Desember

Fix QC Kak Ayu Tgl 4 & 5 Desember
Fix QC Pak Jun Tgl 5 Desember
This commit is contained in:
2025-12-09 10:28:17 +08:00
parent dcb8017594
commit cc318d4d54
28 changed files with 816 additions and 1124 deletions

View File

@@ -220,8 +220,9 @@ export default function ModernNewsNotification({
...styles,
position: "fixed",
bottom: "100px",
right: "24px",
width: "380px",
left: "24px",
width: "90vw",
maxWidth: 380,
maxHeight: "500px",
boxShadow: "0 8px 32px rgba(0,0,0,0.12)",
borderRadius: "16px",
@@ -290,7 +291,7 @@ export default function ModernNewsNotification({
color={item.type === "berita" ? "blue" : "orange"}
variant="light"
>
{item.type === "berita" ? "📰 Berita" : "📢 Pengumuman"}
{item.type === "berita" ? "Berita" : "Pengumuman"}
</Badge>
<IconChevronRight size={16} color="#adb5bd" />
</Group>
@@ -321,8 +322,9 @@ export default function ModernNewsNotification({
...styles,
position: "fixed",
bottom: "100px",
right: "24px",
width: "380px",
left: "24px",
width: "90vw",
maxWidth: 380,
boxShadow: "0 8px 32px rgba(0,0,0,0.15)",
borderRadius: "12px",
overflow: "hidden",
@@ -350,7 +352,6 @@ export default function ModernNewsNotification({
size="md"
color={currentNews?.type === "berita" ? "blue" : "orange"}
variant="light"
leftSection={currentNews?.type === "berita" ? "📰" : "📢"}
>
{currentNews?.type === "berita"
? "Berita Terbaru"

View File

@@ -4,7 +4,7 @@ import indeksKepuasanState from "@/app/admin/(dashboard)/_state/landing-page/ind
import colors from "@/con/colors";
import { BarChart, PieChart } from '@mantine/charts';
import { Box, Button, Center, Container, Flex, Modal, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { useDisclosure, useMediaQuery, useShallowEffect } from "@mantine/hooks";
import { useState } from "react";
import { useProxy } from "valtio/utils";
@@ -25,6 +25,7 @@ function Kepuasan() {
const [donutDataKelompokUmur, setDonutDataKelompokUmur] = useState<ChartDataItem[]>([]);
const [barChartData, setBarChartData] = useState<Array<{ month: string; Responden: number }>>([]);
const [opened, { open, close }] = useDisclosure(false)
const isMobile = useMediaQuery("(max-width: 768px)");
const resetForm = () => {
state.create.form = {
@@ -41,7 +42,7 @@ function Kepuasan() {
indeksKepuasanState.jenisKelaminResponden.findMany.load()
indeksKepuasanState.pilihanRatingResponden.findMany.load()
indeksKepuasanState.kelompokUmurResponden.findMany.load()
},[])
}, [])
const handleSubmit = async () => {
try {
@@ -82,13 +83,13 @@ function Kepuasan() {
// Update gender chart data
setDonutDataJenisKelamin([
{ name: 'Laki-laki', value: totalLaki, color: colors['blue-button'] },
{ name: 'Laki-laki', value: totalLaki, color: '#52ABE3FF' },
{ name: 'Perempuan', value: totalPerempuan, color: '#10A85AFF' },
]);
// Update rating chart data
setDonutDataRating([
{ name: 'Sangat Baik', value: totalSangatBaik, color: colors['blue-button'] },
{ name: 'Sangat Baik', value: totalSangatBaik, color: '#52ABE3FF' },
{ name: 'Baik', value: totalBaik, color: '#10A85AFF' },
{ name: 'Kurang Baik', value: totalKurangBaik, color: '#FFA500' },
{ name: 'Sangat Kurang Baik', value: totalSangatKurangBaik, color: '#FF4500' },
@@ -96,7 +97,7 @@ function Kepuasan() {
// Update age group chart data
setDonutDataKelompokUmur([
{ name: 'Muda', value: totalMuda, color: colors['blue-button'] },
{ name: 'Muda', value: totalMuda, color: '#52ABE3FF' },
{ name: 'Dewasa', value: totalDewasa, color: '#10A85AFF' },
{ name: 'Lansia', value: totalLansia, color: '#FFA500' },
]);
@@ -220,10 +221,13 @@ function Kepuasan() {
<Box style={{ position: 'relative', width: '100%' }}>
<Center>
<PieChart
withLabels
withTooltip
tooltipAnimationDuration={200}
withLabels
labelsPosition="inside" // 👈 ini yang penting!
labelsType="percent"
size={250} // Fixed size in pixels
withLabelsLine
size={isMobile ? 180 : 250} // 👈 kecilkan ukuran di mobile
data={donutDataJenisKelamin}
/>
</Center>
@@ -259,10 +263,10 @@ function Kepuasan() {
withTooltip
tooltipAnimationDuration={200}
withLabels
labelsPosition="outside"
labelsPosition="inside" // 👈 ini yang penting!
labelsType="percent"
withLabelsLine
size={250}
size={isMobile ? 180 : 250} // 👈 kecilkan ukuran di mobile
data={donutDataRating}
/>
</Center>
@@ -302,10 +306,10 @@ function Kepuasan() {
withTooltip
tooltipAnimationDuration={200}
withLabels
labelsPosition="outside"
labelsPosition="inside"// 👈 ini yang penting!
labelsType="percent"
withLabelsLine
size={250}
size={isMobile ? 180 : 250} // 👈 kecilkan ukuran di mobile
data={donutDataKelompokUmur}
/>
</Center>
@@ -494,6 +498,8 @@ function Kepuasan() {
<PieChart
withLabels
withTooltip
labelsPosition="inside"
labelsType="percent"
size={200}
data={donutDataJenisKelamin}
@@ -531,7 +537,8 @@ function Kepuasan() {
withTooltip
tooltipAnimationDuration={200}
withLabels
labelsPosition="outside"
labelsPosition="inside"
labelsType="percent"
withLabelsLine
size={200}
@@ -574,7 +581,8 @@ function Kepuasan() {
withTooltip
tooltipAnimationDuration={200}
withLabels
labelsPosition="outside"
labelsPosition="inside"
labelsType="percent"
withLabelsLine
size={190}
@@ -610,7 +618,7 @@ function Kepuasan() {
<TextInput
label="Nama"
type='text'
placeholder="masukkan nama"
placeholder="Masukkan nama"
value={state.create.form.name}
onChange={(val) => {
state.create.form.name = val.currentTarget.value;
@@ -619,7 +627,7 @@ function Kepuasan() {
<TextInput
label="Tanggal Pengisian"
type="date"
placeholder="masukkan tanggal"
placeholder="Masukkan tanggal"
value={state.create.form.tanggal}
onChange={(val) => {
state.create.form.tanggal = val.currentTarget.value;

View File

@@ -154,7 +154,7 @@ function LandingPage() {
return (
<Stack bg={colors.Bg} p="md" gap="lg">
<Flex gap="lg" wrap={{ base: "wrap", md: "nowrap" }}>
<Flex gap="lg" wrap={{ base: "wrap", md: "nowrap" }} pb={30}>
<Stack w={{ base: "100%", md: "65%" }} gap="lg">
<Card radius="xl" bg={colors.grey[1]} p="lg" mt={10} shadow="xl">
<Stack gap="xl">

View File

@@ -4,7 +4,7 @@ import penghargaanState from "@/app/admin/(dashboard)/_state/desa/penghargaan";
import { Stack, Box, Container, Button, Text, Loader, Paper, Center, ActionIcon } from "@mantine/core";
import { IconAward, IconArrowRight, IconPlayerPlay } from "@tabler/icons-react";
import { useTransitionRouter } from 'next-view-transitions';
import { useEffect, useState } from "react";
import { useEffect, useState, useRef } from "react";
import { useProxy } from "valtio/utils";
import { useMediaQuery } from "@mantine/hooks";
@@ -15,16 +15,35 @@ function Penghargaan() {
const isMobile = useMediaQuery('(max-width: 768px)');
const [isVideoLoaded, setIsVideoLoaded] = useState(false);
const [showVideo, setShowVideo] = useState(true);
const [videoError, setVideoError] = useState(false);
const videoRef = useRef<HTMLVideoElement>(null);
// Opsional: deteksi iOS
const isIOS = typeof window !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent);
// Deteksi iOS dengan lebih akurat
const isIOS = typeof window !== 'undefined' && (
/iPad|iPhone|iPod/.test(navigator.userAgent) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) // iPad dengan iPadOS 13+
);
useEffect(() => {
if (isIOS) {
// Di iOS, jangan andalkan autoplay — tampilkan kontrol
setShowVideo(false);
// Di iOS, coba autoplay dulu, kalau gagal tampilkan fallback
if (isIOS && videoRef.current) {
const playPromise = videoRef.current.play();
if (playPromise !== undefined) {
playPromise
.then(() => {
// Autoplay berhasil
setShowVideo(true);
setIsVideoLoaded(true);
})
.catch(() => {
// Autoplay gagal, tampilkan fallback
setShowVideo(false);
setVideoError(true);
});
}
}
}, []);
}, [isIOS]);
useEffect(() => {
const loadData = async () => {
@@ -38,42 +57,99 @@ function Penghargaan() {
loadData();
}, []);
const handlePlayVideo = () => {
setShowVideo(true);
setVideoError(false);
// Paksa play video setelah user interaction
setTimeout(() => {
if (videoRef.current) {
videoRef.current.play().catch(err => {
console.error("Video play error:", err);
setVideoError(true);
});
}
}, 100);
};
// kalau mobile ambil 1 data aja, kalau desktop ambil 3
const data = state.findMany.data?.slice(0, isMobile ? 1 : 3);
return (
<Stack pos="relative" h="auto" mih={{ base: 500, md: 720 }}>
{showVideo ? (
<Stack pos="relative" h="auto" mih={{ base: 500, md: 720 }} style={{ overflow: 'hidden' }}>
{/* Video Layer */}
{showVideo && !videoError && (
<video
ref={videoRef}
autoPlay
muted
loop
playsInline
webkit-playsinline="true"
preload="auto"
onLoadedData={() => setIsVideoLoaded(true)}
style={{ opacity: isVideoLoaded ? 1 : 0, transition: 'opacity 0.5s' }}
onError={() => {
console.error("Video load error");
setVideoError(true);
setShowVideo(false);
}}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
objectFit: 'cover',
opacity: isVideoLoaded ? 1 : 0,
transition: 'opacity 0.5s ease',
zIndex: 0,
}}
>
<source src="/assets/videos/award.mp4" type="video/mp4" />
</video>
) : (
// Fallback: tampilkan poster + play button
)}
{/* Fallback Image + Play Button */}
{(!showVideo || videoError) && (
<Box
onClick={() => setShowVideo(true)}
onClick={handlePlayVideo}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
backgroundImage: "url('/mangupuraaward.jpeg')",
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
cursor: 'pointer',
zIndex: 0,
}}
>
<Center h="100%">
<ActionIcon size="lg" radius="xl" color="white">
<IconPlayerPlay size={32} />
<Center
style={{
width: '100%',
height: '100%',
background: 'rgba(0,0,0,0.3)', // overlay gelap agar icon terlihat
}}
>
<ActionIcon
size={80}
radius="xl"
variant="filled"
color="blue"
style={{
backgroundColor: 'rgba(255,255,255,0.9)',
boxShadow: '0 8px 32px rgba(0,0,0,0.3)',
}}
>
<IconPlayerPlay size={40} color="var(--mantine-color-blue-6)" />
</ActionIcon>
</Center>
</Box>
)}
{/* Overlay Gradient + Content */}
<Box
style={{
width: "100%",
@@ -152,4 +228,4 @@ function Penghargaan() {
);
}
export default Penghargaan;
export default Penghargaan;