tambahannya

This commit is contained in:
bipproduction
2025-02-19 17:41:56 +08:00
parent 0d4a8329d7
commit 9bde6a2a06
74 changed files with 1079 additions and 186 deletions

View File

@@ -0,0 +1,18 @@
"use client";
import { Button, Stack } from "@mantine/core";
import { Link } from "next-view-transitions";
export default function AdminNav() {
return (
<Stack>
<Button.Group p={"md"}>
<Button component={Link} href="/admin/images">
Images
</Button>
<Button component={Link} href="/admin/csv">
CSV
</Button>
</Button.Group>
</Stack>
);
}

View File

@@ -0,0 +1,139 @@
"use client";
import stateListImage from "@/state/state-list-image";
import {
ActionIcon,
Box,
Button,
Group,
Image,
Pagination,
Paper,
SimpleGrid,
Stack,
Text,
TextInput,
} from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { IconSearch, IconX } from "@tabler/icons-react";
import { motion } from "framer-motion";
import { useState } from "react";
import toast from "react-simple-toasts";
import { useSnapshot } from "valtio";
export default function ListImage() {
const { list, total } = useSnapshot(stateListImage);
const [loading, setLoading] = useState(false);
useShallowEffect(() => {
// get url
console.log(window.location.origin);
stateListImage.load();
}, []);
let timeOut: NodeJS.Timer;
return (
<Stack p={"lg"}>
<Group justify="end">
<TextInput
leftSection={<IconSearch />}
rightSection={
<ActionIcon
variant="transparent"
onClick={() => {
stateListImage.load();
}}
>
<IconX />
</ActionIcon>
}
placeholder="Cari"
onChange={(e) => {
if (timeOut) clearTimeout(timeOut);
timeOut = setTimeout(() => {
stateListImage.load({ search: e.target.value });
}, 200);
}}
/>
</Group>
<SimpleGrid
cols={{
base: 3,
md: 5,
lg: 10,
}}
>
{list &&
list.map((v, k) => {
return (
<Paper key={k} shadow="sm">
<Stack pos={"relative"} gap={0} justify="space-between">
<motion.div
onClick={() => {
// copy to clipboard
navigator.clipboard.writeText(v.url);
toast("Berhasil disalin");
}}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.8 }}
>
<Image
h={100}
src={v.url + "?size=100"}
alt={v.name}
fit="cover"
loading="lazy"
style={{
objectFit: "cover",
objectPosition: "center",
}}
/>
</motion.div>
<Box p={"md"} h={54}>
<Text lineClamp={2} fz={"xs"}>
{v.name}
</Text>
</Box>
<Group justify="end">
<Button.Group>
<Button
onClick={() => {
// copy to clipboard
navigator.clipboard.writeText(v.url);
toast("Berhasil disalin");
}}
variant="subtle"
size="compact-xs"
>
Copy
</Button>
<Button
variant="subtle"
loading={loading}
size="compact-xs"
onClick={() => {
stateListImage.del({ name: v.name }).finally(() => {
setLoading(false);
});
}}
>
delete
</Button>
</Button.Group>
</Group>
</Stack>
</Paper>
);
})}
</SimpleGrid>
{total && (
<Pagination
total={total}
onChange={(e) => {
stateListImage.page = e;
stateListImage.load();
}}
/>
)}
</Stack>
);
}

View File

@@ -0,0 +1,49 @@
"use client";
import ApiFetch from "@/lib/api-fetch";
import { Group, Stack, Text } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { useState } from "react";
import toast from "react-simple-toasts";
export default function UploadCsv() {
return (
<Stack p={"md"}>
<DropUpload />
</Stack>
);
}
function DropUpload() {
const [loading, setLoading] = useState(false);
return (
<Stack justify="center" align="center">
<Group>
<Dropzone
loading={loading}
miw={460}
// accept csv
accept={["text/csv"]}
onDrop={async (droppedFiles) => {
if (droppedFiles.length < 0) {
return toast("Tidak ada file yang diunggah");
}
setLoading(true);
for (const file of droppedFiles) {
await ApiFetch.api["upl-csv-single"].post({
name: file.name,
file,
});
}
setLoading(false);
}}
>
<Stack>
<Text ta="center">Drop Csv here</Text>
</Stack>
</Dropzone>
</Group>
</Stack>
);
}

View File

@@ -0,0 +1,34 @@
"use client";
import ApiFetch from "@/lib/api-fetch";
import stateListImage from "@/state/state-list-image";
import { Stack, Text } from "@mantine/core";
import { Dropzone, IMAGE_MIME_TYPE } from "@mantine/dropzone";
import { useState } from "react";
export default function UploadImage() {
const [loading, setLoading] = useState(false);
return (
<Stack p={"md"}>
<Dropzone
loading={loading}
accept={IMAGE_MIME_TYPE} // Hanya menerima tipe file gambar
onDrop={async (droppedFiles) => {
setLoading(true);
for (const file of droppedFiles) {
await ApiFetch.api["upl-img-single"].post({
file: file,
name: file.name,
});
}
setLoading(false);
stateListImage.load();
}}
>
<Stack align="center">
<Text ta="center">Drop images here</Text>
</Stack>
</Dropzone>
</Stack>
);
}