feat: implement dark mode support & fix Prisma schema validation
- Add dark mode toggle component in admin header - Integrate dark mode store across admin layout and components - Add unified typography and surface components for consistent theming - Implement smooth transitions for dark/light mode switching - Fix Prisma schema: remove @default(null) from DateTime? fields - Update form validation for inovasi, lingkungan, and pendidikan modules - Add form validation and improve UX across multiple admin pages Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Grid, GridCol, Paper, TextInput, Title } from '@mantine/core';
|
||||
import { Grid, GridCol, Paper, TextInput } from '@mantine/core';
|
||||
import { IconSearch } from '@tabler/icons-react';
|
||||
import colors from '@/con/colors';
|
||||
import { useDarkMode } from '@/state/darkModeStore';
|
||||
import { themeTokens } from '@/utils/themeTokens';
|
||||
import { UnifiedTitle } from '@/components/admin/UnifiedTypography';
|
||||
|
||||
type HeaderSearchProps = {
|
||||
title: string;
|
||||
@@ -18,13 +22,16 @@ const HeaderSearch = ({
|
||||
value,
|
||||
onChange,
|
||||
}: HeaderSearchProps) => {
|
||||
const { isDark } = useDarkMode();
|
||||
const tokens = themeTokens(isDark);
|
||||
|
||||
return (
|
||||
<Grid mb={10}>
|
||||
<GridCol span={{ base: 12, md: 9 }}>
|
||||
<Title order={3}>{title}</Title>
|
||||
<UnifiedTitle order={3}>{title}</UnifiedTitle>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 3 }}>
|
||||
<Paper radius="lg" bg={colors['white-1']}>
|
||||
<Paper radius="lg" bg={tokens.colors.bg.surface}>
|
||||
<TextInput
|
||||
radius="lg"
|
||||
placeholder={placeholder}
|
||||
@@ -32,6 +39,16 @@ const HeaderSearch = ({
|
||||
w="100%"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
style={{
|
||||
input: {
|
||||
backgroundColor: tokens.colors.bg.surface,
|
||||
color: tokens.colors.text.primary,
|
||||
borderColor: tokens.colors.border.default,
|
||||
'::placeholder': {
|
||||
color: tokens.colors.text.muted,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Paper>
|
||||
</GridCol>
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Grid, GridCol, Button, Text } from '@mantine/core';
|
||||
import { Grid, GridCol, Button } from '@mantine/core';
|
||||
import { IconCircleDashedPlus } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React from 'react';
|
||||
import { useDarkMode } from '@/state/darkModeStore';
|
||||
import { themeTokens } from '@/utils/themeTokens';
|
||||
import { UnifiedText } from '@/components/admin/UnifiedTypography';
|
||||
|
||||
const JudulList = ({ title = "", href = "#" }) => {
|
||||
const { isDark } = useDarkMode();
|
||||
const tokens = themeTokens(isDark);
|
||||
const router = useRouter();
|
||||
|
||||
const handleNavigate = () => {
|
||||
@@ -16,10 +20,18 @@ const JudulList = ({ title = "", href = "#" }) => {
|
||||
return (
|
||||
<Grid align="center" mb={10}>
|
||||
<GridCol span={{ base: 12, md: 11 }}>
|
||||
<Text fz={"xl"} fw={"bold"}>{title}</Text>
|
||||
<UnifiedText size="body" weight="bold" color="primary">{title}</UnifiedText>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }} ta="right">
|
||||
<Button onClick={handleNavigate} bg={colors['blue-button']}>
|
||||
<Button
|
||||
onClick={handleNavigate}
|
||||
bg={tokens.colors.primary}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${tokens.colors.primary}, ${isDark ? '#60A5FA' : '#4facfe'})`,
|
||||
color: tokens.colors.text.inverse,
|
||||
boxShadow: isDark ? 'none' : `0 4px 15px rgba(79, 172, 254, 0.4)`,
|
||||
}}
|
||||
>
|
||||
<IconCircleDashedPlus size={25} />
|
||||
</Button>
|
||||
</GridCol>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Grid, GridCol, Button, Text, Paper, TextInput } from '@mantine/core';
|
||||
import { Grid, GridCol, Button, Paper, TextInput } from '@mantine/core';
|
||||
import { IconCircleDashedPlus, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React from 'react';
|
||||
import { useDarkMode } from '@/state/darkModeStore';
|
||||
import { themeTokens } from '@/utils/themeTokens';
|
||||
import { UnifiedText } from '@/components/admin/UnifiedTypography';
|
||||
|
||||
type JudulListTabProps = {
|
||||
title: string;
|
||||
@@ -14,17 +16,16 @@ type JudulListTabProps = {
|
||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const JudulListTab = ({
|
||||
title = "",
|
||||
href = "#",
|
||||
placeholder = "pencarian",
|
||||
searchIcon = <IconSearch size={20} />,
|
||||
value,
|
||||
onChange
|
||||
onChange
|
||||
}: JudulListTabProps) => {
|
||||
const { isDark } = useDarkMode();
|
||||
const tokens = themeTokens(isDark);
|
||||
const router = useRouter();
|
||||
|
||||
const handleNavigate = () => {
|
||||
@@ -34,10 +35,17 @@ const JudulListTab = ({
|
||||
return (
|
||||
<Grid mb={10}>
|
||||
<GridCol span={{ base: 12, md: 8 }}>
|
||||
<Text fz={{ base: "md", md: "xl" }} fw={"bold"}>{title}</Text>
|
||||
<UnifiedText
|
||||
size="body"
|
||||
weight="bold"
|
||||
color="primary"
|
||||
style={{ fontSize: 'clamp(1rem, 2vw, 1.25rem)' }}
|
||||
>
|
||||
{title}
|
||||
</UnifiedText>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 9, md: 3 }} ta="right">
|
||||
<Paper radius={"lg"} bg={colors['white-1']}>
|
||||
<Paper radius={"lg"} bg={tokens.colors.bg.surface}>
|
||||
<TextInput
|
||||
radius="lg"
|
||||
placeholder={placeholder}
|
||||
@@ -45,11 +53,29 @@ const JudulListTab = ({
|
||||
w="100%"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
style={{
|
||||
input: {
|
||||
backgroundColor: tokens.colors.bg.surface,
|
||||
color: tokens.colors.text.primary,
|
||||
borderColor: tokens.colors.border.default,
|
||||
'::placeholder': {
|
||||
color: tokens.colors.text.muted,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Paper>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 3, md: 1 }} ta="right">
|
||||
<Button onClick={handleNavigate} bg={colors['blue-button']}>
|
||||
<Button
|
||||
onClick={handleNavigate}
|
||||
bg={tokens.colors.primary}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${tokens.colors.primary}, ${isDark ? '#60A5FA' : '#4facfe'})`,
|
||||
color: tokens.colors.text.inverse,
|
||||
boxShadow: isDark ? 'none' : `0 4px 15px rgba(79, 172, 254, 0.4)`,
|
||||
}}
|
||||
>
|
||||
<IconCircleDashedPlus size={25} />
|
||||
</Button>
|
||||
</GridCol>
|
||||
|
||||
@@ -44,6 +44,21 @@ function EditDigitalSmartVillage() {
|
||||
imageUrl: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -248,8 +263,11 @@ function EditDigitalSmartVillage() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -30,6 +30,22 @@ export default function CreateDesaDigital() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateDesaDigital.create.form.name?.trim() !== '' &&
|
||||
!isHtmlEmpty(stateDesaDigital.create.form.deskripsi) &&
|
||||
file !== null
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateDesaDigital.create.form = {
|
||||
name: '',
|
||||
@@ -227,8 +243,11 @@ export default function CreateDesaDigital() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -44,6 +44,21 @@ function EditInfoTeknologiTepatGuna() {
|
||||
imageUrl: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// Load data pertama kali
|
||||
useEffect(() => {
|
||||
const id = params?.id as string;
|
||||
@@ -260,8 +275,11 @@ function EditInfoTeknologiTepatGuna() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -30,6 +30,22 @@ function CreateInfoTeknologiTepatGuna() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateInfoTekno.create.form.name?.trim() !== '' &&
|
||||
!isHtmlEmpty(stateInfoTekno.create.form.deskripsi) &&
|
||||
file !== null
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateInfoTekno.create.form = {
|
||||
name: '',
|
||||
@@ -202,8 +218,11 @@ function CreateInfoTeknologiTepatGuna() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -44,6 +44,23 @@ function EditKolaborasiInovasi() {
|
||||
kolaborator: "",
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
formData.slug?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi) &&
|
||||
formData.kolaborator?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
// Load data awal dari server
|
||||
useEffect(() => {
|
||||
const loadKolaborasi = async () => {
|
||||
@@ -199,8 +216,11 @@ function EditKolaborasiInovasi() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -16,6 +16,22 @@ function CreateProgramKreatifDesa() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateCreate.create.form.name?.trim() !== '' &&
|
||||
stateCreate.create.form.slug?.trim() !== '' &&
|
||||
!isHtmlEmpty(stateCreate.create.form.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateCreate.create.form = {
|
||||
name: "",
|
||||
@@ -135,8 +151,11 @@ function CreateProgramKreatifDesa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -51,6 +51,11 @@ function EditMitraKolaborasi() {
|
||||
imageUrl: '',
|
||||
});
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return formData.name?.trim() !== '';
|
||||
};
|
||||
|
||||
// Load data ke state lokal sekali saja
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
@@ -263,8 +268,11 @@ function EditMitraKolaborasi() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -29,6 +29,14 @@ function CreateMitraKolaborasi() {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
state.create.form.name?.trim() !== '' &&
|
||||
file !== null
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
state.create.form = {
|
||||
name: '',
|
||||
@@ -181,8 +189,11 @@ function CreateMitraKolaborasi() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -51,6 +51,23 @@ function EditProgramKreatifDesa() {
|
||||
|
||||
const [isDataChanged, setIsDataChanged] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
formData.slug?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi) &&
|
||||
formData.icon?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
// Load data hanya sekali berdasarkan params.id
|
||||
useEffect(() => {
|
||||
const loadProgramKreatif = async () => {
|
||||
@@ -236,8 +253,11 @@ function EditProgramKreatifDesa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -25,6 +25,23 @@ function CreateProgramKreatifDesa() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateCreate.create.form.name?.trim() !== '' &&
|
||||
stateCreate.create.form.icon?.trim() !== '' &&
|
||||
stateCreate.create.form.slug?.trim() !== '' &&
|
||||
!isHtmlEmpty(stateCreate.create.form.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateCreate.create.form = {
|
||||
name: "",
|
||||
@@ -127,8 +144,11 @@ function CreateProgramKreatifDesa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -67,6 +67,23 @@ export default function EditDataLingkunganDesa() {
|
||||
icon: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
formData.jumlah?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi) &&
|
||||
formData.icon?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
// Load data saat komponen mount
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
@@ -211,8 +228,11 @@ export default function EditDataLingkunganDesa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -25,6 +25,23 @@ function CreateDataLingkunganDesa() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateCreate.create.form.name?.trim() !== '' &&
|
||||
stateCreate.create.form.icon?.trim() !== '' &&
|
||||
stateCreate.create.form.jumlah?.trim() !== '' &&
|
||||
!isHtmlEmpty(stateCreate.create.form.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateCreate.create.form = {
|
||||
name: '',
|
||||
@@ -129,8 +146,11 @@ function CreateDataLingkunganDesa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -38,6 +38,21 @@ export default function EditContohKegiatanDesaDarmasaba() {
|
||||
deskripsi: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
!isHtmlEmpty(formData.judul) &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// load data awal
|
||||
useShallowEffect(() => {
|
||||
if (!contohEdukasiState.findById.data) {
|
||||
@@ -156,8 +171,11 @@ export default function EditContohKegiatanDesaDarmasaba() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -27,6 +27,21 @@ export default function EditMateriEdukasiYangDiberikan() {
|
||||
content: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
!isHtmlEmpty(formData.judul) &&
|
||||
!isHtmlEmpty(formData.content)
|
||||
);
|
||||
};
|
||||
|
||||
// Initialize data kalau belum ada
|
||||
useShallowEffect(() => {
|
||||
if (!materiEdukasiState.findById.data) {
|
||||
@@ -139,8 +154,11 @@ export default function EditMateriEdukasiYangDiberikan() {
|
||||
onClick={submit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -28,6 +28,21 @@ export default function EditTujuanEdukasiLingkungan() {
|
||||
deskripsi: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
!isHtmlEmpty(formData.judul) &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// Initialize global state
|
||||
useShallowEffect(() => {
|
||||
if (!tujuanEdukasiState.findById.data) {
|
||||
@@ -147,8 +162,11 @@ export default function EditTujuanEdukasiLingkungan() {
|
||||
onClick={submit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -21,6 +21,11 @@ function EditKategoriKegiatan() {
|
||||
const [originalData, setOriginalData] = useState({ nama: '' });
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return formData.nama?.trim() !== '';
|
||||
};
|
||||
|
||||
// Load data once
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
@@ -126,8 +131,11 @@ function EditKategoriKegiatan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -14,6 +14,11 @@ function CreateKategoriKegiatan() {
|
||||
const stateKategori = useProxy(gotongRoyongState.kategoriKegiatan)
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return stateKategori.create.form.nama?.trim() !== '';
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
stateKategori.findMany.load();
|
||||
}, []);
|
||||
@@ -84,8 +89,11 @@ function CreateKategoriKegiatan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -67,6 +67,27 @@ export default function EditKegiatanDesa() {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsiSingkat) &&
|
||||
!isHtmlEmpty(formData.deskripsiLengkap) &&
|
||||
formData.tanggal?.trim() !== '' &&
|
||||
formData.lokasi?.trim() !== '' &&
|
||||
formData.partisipan !== null &&
|
||||
formData.partisipan >= 0 &&
|
||||
formData.kategoriKegiatanId?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
const formatDateForInput = (dateString: string) => {
|
||||
if (!dateString) return '';
|
||||
return new Date(dateString).toISOString().split('T')[0];
|
||||
@@ -312,8 +333,11 @@ export default function EditKegiatanDesa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -38,6 +38,28 @@ function CreateKegiatanDesa() {
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateKegiatanDesa.create.form.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(stateKegiatanDesa.create.form.deskripsiSingkat) &&
|
||||
stateKegiatanDesa.create.form.partisipan !== null &&
|
||||
stateKegiatanDesa.create.form.partisipan >= 0 &&
|
||||
stateKegiatanDesa.create.form.tanggal !== null &&
|
||||
stateKegiatanDesa.create.form.lokasi?.trim() !== '' &&
|
||||
!isHtmlEmpty(stateKegiatanDesa.create.form.deskripsiLengkap) &&
|
||||
stateKegiatanDesa.create.form.kategoriKegiatanId?.trim() !== '' &&
|
||||
file !== null
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateKegiatanDesa.create.form = {
|
||||
judul: '',
|
||||
@@ -273,8 +295,11 @@ function CreateKegiatanDesa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -27,6 +27,21 @@ function EditBentukKonservasiBerdasarkanAdat() {
|
||||
deskripsi: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
!isHtmlEmpty(formData.judul) &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// Initialize data dari global state
|
||||
useShallowEffect(() => {
|
||||
if (!bentukKonservasiState.findById.data) {
|
||||
@@ -137,8 +152,11 @@ function EditBentukKonservasiBerdasarkanAdat() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -31,6 +31,21 @@ function EditFilosofiTriHitaKarana() {
|
||||
content: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
!isHtmlEmpty(formData.judul) &&
|
||||
!isHtmlEmpty(formData.content)
|
||||
);
|
||||
};
|
||||
|
||||
// Load data dari global state kalau belum ada
|
||||
useShallowEffect(() => {
|
||||
if (!filosofiTriHitaState.findById.data) {
|
||||
@@ -142,8 +157,11 @@ function EditFilosofiTriHitaKarana() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -24,6 +24,21 @@ function EditNilaiKonservasiAdat() {
|
||||
const [formData, setFormData] = useState({ judul: '', deskripsi: '' });
|
||||
const [originalData, setOriginalData] = useState({ judul: '', deskripsi: '' });
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
!isHtmlEmpty(formData.judul) &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// load data awal
|
||||
useShallowEffect(() => {
|
||||
if (!nilaiKonservasiState.findById.data) {
|
||||
@@ -136,8 +151,11 @@ function EditNilaiKonservasiAdat() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -35,6 +35,16 @@ function EditKeteranganBankSampahTerdekat() {
|
||||
lng: 0,
|
||||
});
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
formData.alamat?.trim() !== '' &&
|
||||
formData.namaTempatMaps?.trim() !== '' &&
|
||||
markerPosition !== null
|
||||
);
|
||||
};
|
||||
|
||||
// Load data ketika component mount
|
||||
useEffect(() => {
|
||||
const loadKeterangan = async () => {
|
||||
@@ -197,8 +207,11 @@ function EditKeteranganBankSampahTerdekat() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -19,6 +19,16 @@ function CreateKeteranganBankSampahTerdekat() {
|
||||
const [markerPosition, setMarkerPosition] = useState<{ lat: number; lng: number } | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
keteranganState.create.form.name?.trim() !== '' &&
|
||||
keteranganState.create.form.alamat?.trim() !== '' &&
|
||||
keteranganState.create.form.namaTempatMaps?.trim() !== '' &&
|
||||
markerPosition !== null
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
keteranganState.create.form = {
|
||||
name: "",
|
||||
@@ -135,8 +145,11 @@ function CreateKeteranganBankSampahTerdekat() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -34,6 +34,14 @@ function EditProgramKreatifDesa() {
|
||||
icon: '',
|
||||
});
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
formData.icon?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadProgramKreatif = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -143,8 +151,11 @@ function EditProgramKreatifDesa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -13,6 +13,14 @@ function CreatePengelolaanSampahBankSampah() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateCreate.create.form.name?.trim() !== '' &&
|
||||
stateCreate.create.form.icon?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateCreate.create.form = {
|
||||
name: "",
|
||||
@@ -91,8 +99,11 @@ function CreatePengelolaanSampahBankSampah() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -64,6 +64,23 @@ function EditProgramPenghijauan() {
|
||||
icon: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi) &&
|
||||
formData.icon?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
// Load data program penghijauan
|
||||
useEffect(() => {
|
||||
const loadProgram = async () => {
|
||||
@@ -216,8 +233,11 @@ function EditProgramPenghijauan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -25,6 +25,23 @@ function CreateProgramPenghijauan() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateCreate.create.form.name?.trim() !== '' &&
|
||||
stateCreate.create.form.icon?.trim() !== '' &&
|
||||
stateCreate.create.form.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(stateCreate.create.form.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateCreate.create.form = {
|
||||
name: '',
|
||||
@@ -128,8 +145,11 @@ function CreateProgramPenghijauan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -24,6 +24,21 @@ function EditProgramKreatifDesa() {
|
||||
deskripsi: '',
|
||||
})
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadProgramKreatif = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -160,8 +175,11 @@ function EditProgramKreatifDesa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -16,6 +16,21 @@ function CreateKeunggulanProgram() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateCreate.create.form.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(stateCreate.create.form.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateCreate.create.form = {
|
||||
judul: "",
|
||||
@@ -97,8 +112,11 @@ function CreateKeunggulanProgram() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -42,6 +42,21 @@ function EditFasilitasYangDisediakan() {
|
||||
deskripsi: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// Load data pertama kali
|
||||
useShallowEffect(() => {
|
||||
if (!editState.findById.data) {
|
||||
@@ -76,11 +91,6 @@ function EditFasilitasYangDisediakan() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.judul.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
if (editState.findById.data) {
|
||||
@@ -180,8 +190,11 @@ function EditFasilitasYangDisediakan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -39,6 +39,21 @@ function EditLokasiDanJadwal() {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [originalData, setOriginalData] = useState({ judul: '', deskripsi: '' });
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// Load data sekali
|
||||
useShallowEffect(() => {
|
||||
if (!editState.findById.data) {
|
||||
@@ -73,11 +88,6 @@ function EditLokasiDanJadwal() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.judul.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
if (editState.findById.data) {
|
||||
@@ -178,8 +188,11 @@ function EditLokasiDanJadwal() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -39,6 +39,21 @@ function EditTujuanProgram() {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [originalData, setOriginalData] = useState({ judul: '', deskripsi: '' });
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// load data sekali
|
||||
useShallowEffect(() => {
|
||||
if (!editState.findById.data) editState.findById.initialize();
|
||||
@@ -71,11 +86,6 @@ function EditTujuanProgram() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.judul.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
if (editState.findById.data) {
|
||||
@@ -170,8 +180,11 @@ function EditTujuanProgram() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -28,6 +28,14 @@ export default function EditDataPendidikan() {
|
||||
jumlah: '',
|
||||
});
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.name?.trim() !== '' &&
|
||||
formData.jumlah?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
// Load data saat mount
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
@@ -127,8 +135,11 @@ export default function EditDataPendidikan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -15,6 +15,14 @@ export default function CreateDataPendidikan() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateDPM.create.form.name?.trim() !== '' &&
|
||||
stateDPM.create.form.jumlah?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
stateDPM.create.form = { name: '', jumlah: '' };
|
||||
};
|
||||
@@ -90,8 +98,11 @@ export default function CreateDataPendidikan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -31,6 +31,11 @@ function EditJenjangPendidikan() {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return formData.nama?.trim() !== '';
|
||||
};
|
||||
|
||||
// Load data sekali saat component mount
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
@@ -136,8 +141,11 @@ function EditJenjangPendidikan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -23,6 +23,11 @@ function CreateJenjangPendidikan() {
|
||||
const stateJenjang = useProxy(infoSekolahPaud.jenjangPendidikan);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return stateJenjang.create.form.nama?.trim() !== '';
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
stateJenjang.findMany.load();
|
||||
}, []);
|
||||
@@ -101,8 +106,11 @@ function CreateJenjangPendidikan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -37,6 +37,14 @@ export default function EditLembaga() {
|
||||
jenjangId: '',
|
||||
});
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
form.nama?.trim() !== '' &&
|
||||
form.jenjangId?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
// Load jenjang pendidikan dan data lembaga
|
||||
useEffect(() => {
|
||||
infoSekolahPaud.jenjangPendidikan.findMany.load();
|
||||
@@ -161,8 +169,11 @@ export default function EditLembaga() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -25,6 +25,14 @@ function CreateLembaga() {
|
||||
const stateLembaga = useProxy(infoSekolahPaud.lembagaPendidikan);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateLembaga.create.form.nama?.trim() !== '' &&
|
||||
stateLembaga.create.form.jenjangId?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
stateLembaga.findMany.load();
|
||||
infoSekolahPaud.jenjangPendidikan.findMany.load();
|
||||
@@ -116,8 +124,11 @@ function CreateLembaga() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -40,6 +40,14 @@ function EditPengajar() {
|
||||
lembagaId: ''
|
||||
});
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.nama?.trim() !== '' &&
|
||||
formData.lembagaId?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -157,8 +165,11 @@ function EditPengajar() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -25,6 +25,14 @@ function CreatePengajar() {
|
||||
const stateCreate = useProxy(infoSekolahPaud.pengajar);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateCreate.create.form.nama?.trim() !== '' &&
|
||||
stateCreate.create.form.lembagaId?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
stateCreate.findMany.load();
|
||||
infoSekolahPaud.lembagaPendidikan.findMany.load();
|
||||
@@ -116,8 +124,11 @@ function CreatePengajar() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -42,6 +42,14 @@ function EditSiswa() {
|
||||
lembagaId: '',
|
||||
});
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.nama?.trim() !== '' &&
|
||||
formData.lembagaId?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
// Load data siswa
|
||||
useEffect(() => {
|
||||
const loadSiswa = async () => {
|
||||
@@ -166,8 +174,11 @@ function EditSiswa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -25,6 +25,14 @@ function CreateSiswa() {
|
||||
const stateCreate = useProxy(infoSekolahPaud.siswa);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
stateCreate.create.form.nama?.trim() !== '' &&
|
||||
stateCreate.create.form.lembagaId?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
stateCreate.findMany.load();
|
||||
infoSekolahPaud.lembagaPendidikan.findMany.load();
|
||||
@@ -115,8 +123,11 @@ function CreateSiswa() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -37,6 +37,21 @@ function EditJenisProgramYangDiselenggarakan() {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [originalData, setOriginalData] = useState({ judul: '', content: '' });
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.content)
|
||||
);
|
||||
};
|
||||
|
||||
// Load data pertama kali
|
||||
useShallowEffect(() => {
|
||||
if (!editState.findById.data) {
|
||||
@@ -71,11 +86,6 @@ function EditJenisProgramYangDiselenggarakan() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.judul.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
if (editState.findById.data) {
|
||||
@@ -168,8 +178,11 @@ function EditJenisProgramYangDiselenggarakan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -45,6 +45,21 @@ function EditTempatKegiatan() {
|
||||
deskripsi: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// load data pertama kali
|
||||
useShallowEffect(() => {
|
||||
if (!editState.findById.data) {
|
||||
@@ -79,11 +94,6 @@ function EditTempatKegiatan() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.judul.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
if (editState.findById.data) {
|
||||
@@ -177,8 +187,11 @@ function EditTempatKegiatan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -38,6 +38,21 @@ function EditTujuanProgram() {
|
||||
deskripsi: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// Load data pertama kali
|
||||
useShallowEffect(() => {
|
||||
if (!editState.findById.data) {
|
||||
@@ -72,11 +87,6 @@ function EditTujuanProgram() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.judul.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!editState.findById.data) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
@@ -163,8 +173,11 @@ function EditTujuanProgram() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -38,6 +38,22 @@ function EditPerpustakaanDigital() {
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi) &&
|
||||
formData.kategoriId?.trim() !== ''
|
||||
);
|
||||
};
|
||||
|
||||
// Load kategori & data awal
|
||||
useEffect(() => {
|
||||
perpustakaanDigitalState.kategoriBuku.findManyAll.load();
|
||||
@@ -254,8 +270,11 @@ function EditPerpustakaanDigital() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -18,6 +18,23 @@ function CreateDataPerpustakaan() {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
createState.create.form.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(createState.create.form.deskripsi) &&
|
||||
createState.create.form.kategoriId?.trim() !== '' &&
|
||||
file !== null
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
perpustakaanDigitalState.kategoriBuku.findManyAll.load();
|
||||
}, []);
|
||||
@@ -196,8 +213,11 @@ function CreateDataPerpustakaan() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -23,6 +23,11 @@ function EditKategoriBuku() {
|
||||
const [formData, setFormData] = useState({ name: '' });
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return formData.name?.trim() !== '';
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadKategori = async () => {
|
||||
const id = params?.id as string;
|
||||
@@ -120,8 +125,11 @@ function EditKategoriBuku() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -13,6 +13,11 @@ function CreateKategoriBuku() {
|
||||
const router = useRouter();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return createState.create.form.name?.trim() !== '';
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
createState.create.form = {
|
||||
name: "",
|
||||
@@ -81,8 +86,11 @@ function CreateKategoriBuku() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -70,6 +70,26 @@ function EditPeminjam() {
|
||||
catatan: "",
|
||||
})
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.nama?.trim() !== '' &&
|
||||
formData.noTelp?.trim() !== '' &&
|
||||
formData.alamat?.trim() !== '' &&
|
||||
formData.bukuId?.trim() !== '' &&
|
||||
formData.tanggalPinjam?.trim() !== '' &&
|
||||
formData.status?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.catatan)
|
||||
);
|
||||
};
|
||||
|
||||
useShallowEffect(() => {
|
||||
perpustakaanDigitalState.dataPerpustakaan.findManyAll.load()
|
||||
})
|
||||
@@ -296,8 +316,11 @@ function EditPeminjam() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -50,6 +50,21 @@ function EditTujuanProgram() {
|
||||
});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// load data once
|
||||
useShallowEffect(() => {
|
||||
if (!editState.findById.data) editState.findById.initialize();
|
||||
@@ -85,11 +100,6 @@ function EditTujuanProgram() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.judul.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
if (editState.findById.data) {
|
||||
@@ -186,8 +196,11 @@ function EditTujuanProgram() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -38,6 +38,21 @@ function EditTujuanProgram() {
|
||||
deskripsi: '',
|
||||
});
|
||||
|
||||
// Helper function to check if HTML content is empty
|
||||
const isHtmlEmpty = (html: string) => {
|
||||
// Remove all HTML tags and check if there's any text content
|
||||
const textContent = html.replace(/<[^>]*>/g, '').trim();
|
||||
return textContent === '';
|
||||
};
|
||||
|
||||
// Check if form is valid
|
||||
const isFormValid = () => {
|
||||
return (
|
||||
formData.judul?.trim() !== '' &&
|
||||
!isHtmlEmpty(formData.deskripsi)
|
||||
);
|
||||
};
|
||||
|
||||
// Load data pertama kali
|
||||
useShallowEffect(() => {
|
||||
if (!editState.findById.data) {
|
||||
@@ -72,11 +87,6 @@ function EditTujuanProgram() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.judul.trim()) {
|
||||
toast.error('Judul wajib diisi');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
if (editState.findById.data) {
|
||||
@@ -166,8 +176,11 @@ function EditTujuanProgram() {
|
||||
onClick={handleSubmit}
|
||||
radius="md"
|
||||
size="md"
|
||||
disabled={!isFormValid() || isSubmitting}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
background: !isFormValid() || isSubmitting
|
||||
? `linear-gradient(135deg, #cccccc, #eeeeee)`
|
||||
: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||
color: '#fff',
|
||||
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import colors from "@/con/colors";
|
||||
import { authStore } from "@/store/authStore";
|
||||
import { useDarkMode } from "@/state/darkModeStore";
|
||||
import { themeTokens, getActiveStateStyles } from "@/utils/themeTokens";
|
||||
import { DarkModeToggle } from "@/components/admin/DarkModeToggle";
|
||||
import {
|
||||
ActionIcon,
|
||||
AppShell,
|
||||
@@ -33,13 +35,21 @@ import { useEffect, useState } from "react";
|
||||
import { getNavbar } from "./(dashboard)/user&role/_com/dynamicNavbar";
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
const [opened, { toggle, close }] = useDisclosure(); // ✅ Tambahkan 'close'
|
||||
const { isDark } = useDarkMode();
|
||||
const tokens = themeTokens(isDark);
|
||||
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [opened, { toggle, close }] = useDisclosure();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
||||
const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true);
|
||||
const router = useRouter();
|
||||
const segments = useSelectedLayoutSegments().map((s) => _.lowerCase(s));
|
||||
|
||||
|
||||
// Ensure component is mounted on client side
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUser = async () => {
|
||||
@@ -74,7 +84,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
});
|
||||
|
||||
const currentPath = window.location.pathname;
|
||||
|
||||
|
||||
if (currentPath === '/admin') {
|
||||
const expectedPath = getRedirectPath(Number(data.user.roleId));
|
||||
console.log('🔄 Redirecting from /admin to:', expectedPath);
|
||||
@@ -112,11 +122,11 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
if (loading || !mounted) {
|
||||
return (
|
||||
<AppShell>
|
||||
<AppShellMain>
|
||||
<Center h="100vh">
|
||||
<Center h="100vh" bg="#f6f9fc">
|
||||
<Loader />
|
||||
</Center>
|
||||
</AppShellMain>
|
||||
@@ -132,7 +142,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
try {
|
||||
setIsLoggingOut(true);
|
||||
|
||||
const response = await fetch('/api/auth/logout', {
|
||||
const response = await fetch('/api/auth/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include'
|
||||
});
|
||||
@@ -158,10 +168,9 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ Handler untuk menutup mobile menu saat navigasi
|
||||
const handleNavClick = (path: string) => {
|
||||
router.push(path);
|
||||
close(); // Tutup mobile menu
|
||||
close();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -178,11 +187,16 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
}}
|
||||
padding="md"
|
||||
>
|
||||
{/*
|
||||
HEADER / TOPBAR
|
||||
Spec: Background gradient, border bawah wajib
|
||||
*/}
|
||||
<AppShellHeader
|
||||
style={{
|
||||
background: "linear-gradient(90deg, #ffffff, #f9fbff)",
|
||||
borderBottom: `1px solid ${colors["blue-button"]}20`,
|
||||
background: mounted ? tokens.colors.bg.header : 'linear-gradient(90deg, #ffffff, #f9fbff)',
|
||||
borderBottom: `1px solid ${mounted ? tokens.colors.border.soft : '#e9ecef'}`,
|
||||
padding: '0 16px',
|
||||
transition: 'background 0.3s ease, border-color 0.3s ease',
|
||||
}}
|
||||
px={{ base: 'sm', sm: 'md' }}
|
||||
py={{ base: 'xs', sm: 'sm' }}
|
||||
@@ -198,30 +212,49 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
loading="lazy"
|
||||
style={{ minWidth: '32px', height: 'auto' }}
|
||||
/>
|
||||
<Text fw={700} c={colors["blue-button"]} fz={{ base: 'md', sm: 'xl' }}>
|
||||
<Text fw={700} c={mounted ? tokens.colors.text.brand : '#0A4E78'} fz={{ base: 'md', sm: 'xl' }}>
|
||||
Admin Darmasaba
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Group gap="xs">
|
||||
{/* Dark Mode Toggle */}
|
||||
<DarkModeToggle variant="light" size="lg" showTooltip tooltipPosition="bottom" />
|
||||
|
||||
{!desktopOpened && (
|
||||
<Tooltip label="Buka Navigasi" position="bottom" withArrow>
|
||||
<ActionIcon variant="light" radius="xl" size="lg" onClick={toggleDesktop} color={colors["blue-button"]}>
|
||||
<ActionIcon variant="light" radius="xl" size="lg" onClick={toggleDesktop} color={mounted ? tokens.colors.primary : '#3B82F6'}>
|
||||
<IconChevronRight />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="md" color={colors["blue-button"]} mr="xs" />
|
||||
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="md" color={mounted ? tokens.colors.text.brand : '#0A4E78'} mr="xs" />
|
||||
|
||||
<Tooltip label="Kembali ke Website Desa" position="bottom" withArrow>
|
||||
<ActionIcon onClick={() => router.push("/darmasaba")} color={colors["blue-button"]} radius="xl" size="lg" variant="gradient" gradient={{ from: colors["blue-button"], to: "#228be6" }}>
|
||||
<ActionIcon
|
||||
onClick={() => router.push("/darmasaba")}
|
||||
color={mounted ? tokens.colors.primary : '#3B82F6'}
|
||||
radius="xl"
|
||||
size="lg"
|
||||
variant="gradient"
|
||||
gradient={mounted ? tokens.colors.gradient : { from: '#3B82F6', to: '#60A5FA' }}
|
||||
>
|
||||
<Image src="/assets/images/darmasaba-icon.png" alt="Logo Darmasaba" w={20} h={20} radius="md" loading="lazy" style={{ minWidth: '20px', height: 'auto' }} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label="Keluar" position="bottom" withArrow>
|
||||
<ActionIcon onClick={handleLogout} color={colors["blue-button"]} radius="xl" size="lg" variant="gradient" gradient={{ from: colors["blue-button"], to: "#228be6" }} loading={isLoggingOut} disabled={isLoggingOut}>
|
||||
<ActionIcon
|
||||
onClick={handleLogout}
|
||||
color={mounted ? tokens.colors.primary : '#3B82F6'}
|
||||
radius="xl"
|
||||
size="lg"
|
||||
variant="gradient"
|
||||
gradient={mounted ? tokens.colors.gradient : { from: '#3B82F6', to: '#60A5FA' }}
|
||||
loading={isLoggingOut}
|
||||
disabled={isLoggingOut}
|
||||
>
|
||||
<IconLogout2 size={22} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
@@ -229,47 +262,104 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
</Group>
|
||||
</AppShellHeader>
|
||||
|
||||
<AppShellNavbar component={ScrollArea} style={{ background: "#ffffff", borderRight: `1px solid ${colors["blue-button"]}20` }} p={{ base: 'xs', sm: 'sm' }}>
|
||||
{/*
|
||||
SIDEBAR / NAVBAR
|
||||
Spec: Background --bg-app, active state dengan accent bar
|
||||
*/}
|
||||
<AppShellNavbar
|
||||
component={ScrollArea}
|
||||
style={{
|
||||
background: mounted ? tokens.colors.bg.app : '#ffffff',
|
||||
borderRight: `1px solid ${mounted ? tokens.colors.border.soft : '#e9ecef'}`,
|
||||
transition: 'background 0.3s ease, border-color 0.3s ease',
|
||||
}}
|
||||
p={{ base: 'xs', sm: 'sm' }}
|
||||
>
|
||||
<AppShell.Section p="sm">
|
||||
{currentNav.map((v, k) => {
|
||||
const isParentActive = segments.includes(_.lowerCase(v.name));
|
||||
return (
|
||||
<NavLink
|
||||
key={k}
|
||||
defaultOpened={isParentActive}
|
||||
c={isParentActive ? colors["blue-button"] : "gray"}
|
||||
label={<Text fw={isParentActive ? 600 : 400} fz="sm">{v.name}</Text>}
|
||||
style={{ borderRadius: rem(10), marginBottom: rem(4), transition: "background 150ms ease" }}
|
||||
styles={{ root: { '&:hover': { backgroundColor: 'rgba(25, 113, 194, 0.05)' } } }}
|
||||
variant="light"
|
||||
<NavLink
|
||||
key={k}
|
||||
defaultOpened={isParentActive}
|
||||
c={mounted && isParentActive ? tokens.colors.primary : mounted && isDark ? '#E5E7EB' : tokens.colors.text.secondary}
|
||||
label={
|
||||
<Text
|
||||
fw={isParentActive ? 600 : 400}
|
||||
fz="sm"
|
||||
style={{
|
||||
color: mounted && isDark ? '#E5E7EB' : 'inherit',
|
||||
transition: 'color 150ms ease',
|
||||
}}
|
||||
>
|
||||
{v.name}
|
||||
</Text>
|
||||
}
|
||||
style={{
|
||||
borderRadius: rem(10),
|
||||
marginBottom: rem(4),
|
||||
transition: "background 150ms ease",
|
||||
...(mounted && isParentActive && !isDark && {
|
||||
borderLeft: `3px solid ${tokens.colors.primary}`,
|
||||
}),
|
||||
}}
|
||||
styles={{
|
||||
root: {
|
||||
'&:hover': {
|
||||
backgroundColor: mounted && isDark ? '#1E293B' : tokens.colors.bg.hover,
|
||||
},
|
||||
...(mounted && isParentActive && isDark && {
|
||||
backgroundColor: 'rgba(59,130,246,0.25)',
|
||||
borderLeft: `3px solid ${tokens.colors.primary}`,
|
||||
}),
|
||||
}
|
||||
}}
|
||||
variant="light"
|
||||
active={isParentActive}
|
||||
>
|
||||
{v.children.map((child, key) => {
|
||||
const isChildActive = segments.includes(_.lowerCase(child.name));
|
||||
return (
|
||||
<NavLink
|
||||
key={key}
|
||||
// ✅ PERBAIKAN: Gunakan onClick untuk handle navigasi dan close menu
|
||||
<NavLink
|
||||
key={key}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleNavClick(child.path);
|
||||
}}
|
||||
href={child.path}
|
||||
c={isChildActive ? colors["blue-button"] : "gray"}
|
||||
label={<Text fw={isChildActive ? 600 : 400} fz="sm">{child.name}</Text>}
|
||||
styles={{
|
||||
root: {
|
||||
borderRadius: rem(8),
|
||||
marginBottom: rem(2),
|
||||
transition: 'background 150ms ease',
|
||||
padding: '6px 12px',
|
||||
'&:hover': {
|
||||
backgroundColor: isChildActive ? 'rgba(25, 113, 194, 0.15)' : 'rgba(25, 113, 194, 0.05)'
|
||||
},
|
||||
...(isChildActive && { backgroundColor: 'rgba(25, 113, 194, 0.1)' })
|
||||
}
|
||||
}}
|
||||
active={isChildActive}
|
||||
c={mounted && isChildActive ? tokens.colors.primary : mounted && isDark ? '#E5E7EB' : tokens.colors.text.secondary}
|
||||
label={
|
||||
<Text
|
||||
fw={isChildActive ? 600 : 400}
|
||||
fz="sm"
|
||||
style={{
|
||||
color: mounted && isDark ? '#E5E7EB' : 'inherit',
|
||||
transition: 'color 150ms ease',
|
||||
}}
|
||||
>
|
||||
{child.name}
|
||||
</Text>
|
||||
}
|
||||
styles={{
|
||||
root: {
|
||||
borderRadius: rem(8),
|
||||
marginBottom: rem(2),
|
||||
transition: 'background 150ms ease',
|
||||
padding: '6px 12px',
|
||||
'&:hover': {
|
||||
backgroundColor: mounted && isDark ? 'rgba(255, 255, 255, 0.05)' : tokens.colors.bg.hover,
|
||||
},
|
||||
...(mounted && isChildActive && isDark && {
|
||||
backgroundColor: 'rgba(59,130,246,0.15)',
|
||||
borderLeft: `2px solid ${tokens.colors.primary}`,
|
||||
}),
|
||||
...(mounted && isChildActive && !isDark && {
|
||||
backgroundColor: tokens.colors.bg.hover,
|
||||
}),
|
||||
}
|
||||
}}
|
||||
active={isChildActive}
|
||||
variant="subtle"
|
||||
component={Link}
|
||||
/>
|
||||
);
|
||||
@@ -282,7 +372,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
<AppShell.Section py="md">
|
||||
<Group justify="end" pr="sm">
|
||||
<Tooltip label={desktopOpened ? "Tutup Navigasi" : "Buka Navigasi"} position="top" withArrow>
|
||||
<ActionIcon variant="light" radius="xl" size="lg" onClick={toggleDesktop} color={colors["blue-button"]}>
|
||||
<ActionIcon variant="light" radius="xl" size="lg" onClick={toggleDesktop} color={mounted ? tokens.colors.primary : '#3B82F6'}>
|
||||
<IconChevronLeft />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
@@ -290,7 +380,17 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
</AppShell.Section>
|
||||
</AppShellNavbar>
|
||||
|
||||
<AppShellMain style={{ background: "linear-gradient(180deg, #fdfdfd, #f6f9fc)", minHeight: "100vh" }}>
|
||||
{/*
|
||||
MAIN CONTENT
|
||||
Spec: Background --bg-base
|
||||
*/}
|
||||
<AppShellMain
|
||||
style={{
|
||||
background: mounted ? tokens.colors.bg.base : '#f6f9fc',
|
||||
minHeight: "100vh",
|
||||
transition: 'background 0.3s ease',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AppShellMain>
|
||||
</AppShell>
|
||||
|
||||
Reference in New Issue
Block a user