feat: admin app information

deskripsi:
- feature tambah stiker
This commit is contained in:
2025-05-15 15:04:30 +08:00
parent fbea35eef9
commit bc10b80139
16 changed files with 831 additions and 113 deletions

View File

@@ -0,0 +1,125 @@
-- AlterTable
ALTER TABLE "MasterBidangBisnis" ADD COLUMN "slug" TEXT NOT NULL DEFAULT 'NULL';
-- CreateTable
CREATE TABLE "Portofolio_BidangDanSubBidangBisnis" (
"id" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"portofolioId" TEXT,
"masterBidangBisnisId" TEXT,
"masterSubBidangBisnisId" TEXT,
CONSTRAINT "Portofolio_BidangDanSubBidangBisnis_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "MasterSubBidangBisnis" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"slug" TEXT NOT NULL DEFAULT 'NULL',
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"masterBidangBisnisId" TEXT,
CONSTRAINT "MasterSubBidangBisnis_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "MasterStatusTransaksi" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "MasterStatusTransaksi_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "MasterEmotions" (
"id" SERIAL NOT NULL,
"label" TEXT NOT NULL,
"value" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "MasterEmotions_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "EventSponsor" (
"id" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"name" TEXT NOT NULL,
"isTransfer" BOOLEAN DEFAULT false,
"fileName" TEXT NOT NULL,
"fileExt" TEXT,
"fileId" TEXT NOT NULL,
"authorId" TEXT,
"eventId" TEXT,
CONSTRAINT "EventSponsor_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "EventTransaksi" (
"id" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"nominal" INTEGER NOT NULL,
"status" TEXT NOT NULL,
"transferImageId" TEXT,
"masterBankId" TEXT,
"authorId" TEXT,
"eventId" TEXT,
"eventSponsorId" TEXT,
"masterStatusTransaksiId" TEXT,
CONSTRAINT "EventTransaksi_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "MasterEmotions_value_key" ON "MasterEmotions"("value");
-- CreateIndex
CREATE UNIQUE INDEX "EventTransaksi_eventSponsorId_key" ON "EventTransaksi"("eventSponsorId");
-- AddForeignKey
ALTER TABLE "Portofolio_BidangDanSubBidangBisnis" ADD CONSTRAINT "Portofolio_BidangDanSubBidangBisnis_portofolioId_fkey" FOREIGN KEY ("portofolioId") REFERENCES "Portofolio"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Portofolio_BidangDanSubBidangBisnis" ADD CONSTRAINT "Portofolio_BidangDanSubBidangBisnis_masterBidangBisnisId_fkey" FOREIGN KEY ("masterBidangBisnisId") REFERENCES "MasterBidangBisnis"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Portofolio_BidangDanSubBidangBisnis" ADD CONSTRAINT "Portofolio_BidangDanSubBidangBisnis_masterSubBidangBisnisI_fkey" FOREIGN KEY ("masterSubBidangBisnisId") REFERENCES "MasterSubBidangBisnis"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "MasterSubBidangBisnis" ADD CONSTRAINT "MasterSubBidangBisnis_masterBidangBisnisId_fkey" FOREIGN KEY ("masterBidangBisnisId") REFERENCES "MasterBidangBisnis"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "EventSponsor" ADD CONSTRAINT "EventSponsor_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "EventSponsor" ADD CONSTRAINT "EventSponsor_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "Event"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "EventTransaksi" ADD CONSTRAINT "EventTransaksi_masterBankId_fkey" FOREIGN KEY ("masterBankId") REFERENCES "MasterBank"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "EventTransaksi" ADD CONSTRAINT "EventTransaksi_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "EventTransaksi" ADD CONSTRAINT "EventTransaksi_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "Event"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "EventTransaksi" ADD CONSTRAINT "EventTransaksi_eventSponsorId_fkey" FOREIGN KEY ("eventSponsorId") REFERENCES "EventSponsor"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "EventTransaksi" ADD CONSTRAINT "EventTransaksi_masterStatusTransaksiId_fkey" FOREIGN KEY ("masterStatusTransaksiId") REFERENCES "MasterStatusTransaksi"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@@ -0,0 +1,30 @@
-- CreateTable
CREATE TABLE "Stiker" (
"id" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"name" TEXT,
"fileName" TEXT,
"fileExt" TEXT,
"fileId" TEXT NOT NULL,
CONSTRAINT "Stiker_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "_StikerEmotions" (
"A" INTEGER NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_StikerEmotions_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateIndex
CREATE INDEX "_StikerEmotions_B_index" ON "_StikerEmotions"("B");
-- AddForeignKey
ALTER TABLE "_StikerEmotions" ADD CONSTRAINT "_StikerEmotions_A_fkey" FOREIGN KEY ("A") REFERENCES "MasterEmotions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_StikerEmotions" ADD CONSTRAINT "_StikerEmotions_B_fkey" FOREIGN KEY ("B") REFERENCES "Stiker"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,28 @@
/*
Warnings:
- You are about to drop the `Stiker` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "_StikerEmotions" DROP CONSTRAINT "_StikerEmotions_B_fkey";
-- DropTable
DROP TABLE "Stiker";
-- CreateTable
CREATE TABLE "Sticker" (
"id" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"name" TEXT,
"fileName" TEXT,
"fileExt" TEXT,
"fileId" TEXT NOT NULL,
CONSTRAINT "Sticker_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "_StikerEmotions" ADD CONSTRAINT "_StikerEmotions_B_fkey" FOREIGN KEY ("B") REFERENCES "Sticker"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -1,3 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"

View File

@@ -152,8 +152,6 @@ model Portofolio_MediaSosial {
portofolioId String? @unique
}
// ------------------- MASTER -------------------------- //
model Portofolio_BidangDanSubBidangBisnis {
id String @id @default(cuid())
isActive Boolean @default(true)
@@ -168,6 +166,8 @@ model Portofolio_BidangDanSubBidangBisnis {
masterSubBidangBisnisId String?
}
// ------------------- MASTER -------------------------- //
model MasterBidangBisnis {
id String @id @default(uuid())
name String
@@ -222,6 +222,16 @@ model MasterStatusTransaksi {
EventTransaksi EventTransaksi[]
}
model MasterEmotions {
id Int @id @default(autoincrement())
label String
value String @unique
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Sticker Sticker[] @relation("StikerEmotions")
}
// -------------------- INVESTASI --------------------- //
// Table investasi / saham
model Investasi {
@@ -1046,3 +1056,16 @@ model EventTransaksi {
MasterStatusTransaksi MasterStatusTransaksi? @relation(fields: [masterStatusTransaksiId], references: [id])
masterStatusTransaksiId String?
}
model Sticker {
id String @id @default(cuid())
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String?
fileName String?
fileExt String?
fileId String
MasterEmotions MasterEmotions[] @relation("StikerEmotions")
}

View File

@@ -0,0 +1,33 @@
import { NextResponse } from "next/server";
import { prisma } from "@/lib";
export async function GET(request: Request) {
const method = request.method;
if (method !== "GET") {
return NextResponse.json(
{ success: false, message: "Method not allowed" },
{ status: 405 }
);
}
try {
const response = await prisma.masterEmotions.findMany({
orderBy: {
createdAt: "asc",
},
where: {
isActive: true,
},
});
return NextResponse.json(
{ success: true, data: response },
{ status: 200 }
);
} catch (error) {
console.error("Error fetching master emotions:", error);
return NextResponse.json(
{ success: false, message: "Failed to fetch master emotions" },
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,80 @@
import { NextResponse } from "next/server";
import { prisma } from "@/lib";
export { POST, GET };
interface IPostSticker {
fileId: string;
emotions: string[];
}
async function POST(request: Request) {
const method = request.method;
if (method !== "POST") {
return NextResponse.json(
{ success: false, message: "Method not allowed" },
{ status: 405 }
);
}
try {
const { fileId, emotions } = await request.json();
const newStiker = await prisma.sticker.create({
data: {
fileId,
MasterEmotions: {
connect: emotions.map((value: string) => ({ value })), // id = number[]
},
},
});
if (!newStiker) {
return NextResponse.json(
{ success: false, message: "Gagal membuat stiker" },
{ status: 400 }
);
}
return NextResponse.json(
{ success: true, message: "Berhasil membuat stiker" },
{ status: 200 }
);
} catch (error) {
console.error("Error create sticker", error);
return NextResponse.json(
{ success: false, message: "Failed to create sticker" },
{ status: 500 }
);
}
}
async function GET(request: Request) {
const method = request.method;
if (method !== "GET") {
return NextResponse.json(
{ success: false, message: "Method not allowed" },
{ status: 405 }
);
}
try {
const sticker = await prisma.sticker.findMany({
include: {
MasterEmotions: true,
},
});
return NextResponse.json(
{ success: true, message: "Success get data sticker", data: sticker },
{ status: 200 }
);
} catch (error) {
console.error("Error get data sticker", error);
return NextResponse.json(
{ success: false, message: "Error get data sticker" },
{ status: 500 }
);
}
}

View File

@@ -28,6 +28,7 @@ import { master_nama_bank } from "@/bin/seeder/master";
import { master_status_transaksi } from "@/bin/seeder/master";
import pLimit from "p-limit";
import { master_new_bidang_bisnis } from "@/bin/seeder/master";
import { master_emotions } from "@/bin/seeder/master";
async function masterUserRole() {
for (let i of userRole) {
@@ -614,6 +615,20 @@ async function masterStatusTransaksi() {
console.log("masterStatusTransaksi success");
}
async function masterEmotions() {
await Promise.all(
master_emotions.map((a) =>
prisma.masterEmotions.upsert({
where: { value: a.value },
create: { value: a.value, label: a.label },
update: { value: a.value, label: a.label },
})
)
);
console.log("masterEmotions success");
}
const listSeederQueue = [
masterUserRole,
seederUser,
@@ -643,6 +658,7 @@ const listSeederQueue = [
masterKategoriApp,
masterInvestasiNewTransaksiStatus,
masterStatusTransaksi,
masterEmotions,
];
const limit = pLimit(1);

View File

@@ -3,6 +3,7 @@ export {
apiGetMasterBidangBisnis,
apiGetMasterStatusTransaksi,
apiGetAdminContact,
apiGetMasterEmotions,
};
const apiGetMasterBank = async () => {
@@ -90,3 +91,20 @@ const apiGetAdminContact = async () => {
throw error; // Re-throw the error to handle it in the calling function
}
};
const apiGetMasterEmotions = async () => {
const { token } = await fetch("/api/get-cookie").then((res) => res.json());
if (!token) return await token.json().catch(() => null);
const response = await fetch(`/api/master/emotions`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
});
return await response.json().catch(() => null);
};

View File

@@ -0,0 +1,17 @@
import { Prisma } from "@prisma/client";
export type ISticker = Prisma.StickerGetPayload<{
select: {
id: true;
name: true;
fileId: true;
emotions: true;
};
include: {
MasterEmotions: {
select: {
value: true;
};
};
};
}>;

View File

@@ -0,0 +1,67 @@
export const apiAdminCreateSticker = async ({ data }: { data: any }) => {
try {
// Fetch token from cookie
const { token } = await fetch("/api/get-cookie").then((res) => res.json());
if (!token) {
console.error("No token found");
return null;
}
const response = await fetch(`/api/sticker`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(data),
});
// Check if the response is OK
if (!response.ok) {
const errorData = await response.json().catch(() => null);
console.error("Failed to create sticker", response.statusText, errorData);
throw new Error(errorData?.message || "Failed to create sticker");
}
// Return the JSON response
return await response.json();
} catch (error) {
console.error("Error create sticker", error);
throw error; // Re-throw the error to handle it in the calling function
}
};
export const apiAdminGetSticker = async () => {
try {
// Fetch token from cookie
const { token } = await fetch("/api/get-cookie").then((res) => res.json());
if (!token) {
console.error("No token found");
return null;
}
const response = await fetch(`/api/sticker`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
});
// Check if the response is OK
if (!response.ok) {
const errorData = await response.json().catch(() => null);
console.error("Failed to get sticker", response.statusText, errorData);
throw new Error(errorData?.message || "Failed to get sticker");
}
// Return the JSON response
return await response.json();
} catch (error) {
console.error("Error get sticker", error);
throw error; // Re-throw the error to handle it in the calling function
}
};

View File

@@ -1,21 +1,24 @@
"use client";
import {
AspectRatio,
Box,
Button,
Center,
Chip,
Group,
Image,
Paper,
Select,
Stack,
TextInput,
} from "@mantine/core";
import { ComponentAdminGlobal_TitlePage } from "../../_admin_global/_component";
import { Admin_ComponentBoxStyle } from "../../_admin_global/_component/comp_admin_boxstyle";
import { Admin_V3_ComponentBreakpoint } from "../../_components_v3/comp_simple_grid_breakpoint";
import { pathAssetImage } from "@/lib";
import { DIRECTORY_ID, pathAssetImage } from "@/lib";
import Admin_ComponentBackButton from "../../_admin_global/back_button";
import { IconCheck, IconUpload } from "@tabler/icons-react";
import { IconCheck, IconPhoto, IconUpload } from "@tabler/icons-react";
import {
AdminColor,
MainColor,
@@ -23,27 +26,129 @@ import {
import { baseStylesTextInput } from "@/app_modules/_global/lib/base_style_text_input";
import { useState } from "react";
import Component_V3_Label_TextInput from "@/app_modules/_global/component/new/comp_V3_label_text_input";
import {
ComponentGlobal_BoxInformation,
ComponentGlobal_BoxUploadImage,
ComponentGlobal_ButtonUploadFileImage,
} from "@/app_modules/_global/component";
import { useRouter } from "next/navigation";
import { apiGetMasterEmotions } from "@/app_modules/_global/lib/api_fetch_master";
import { useShallowEffect } from "@mantine/hooks";
import CustomSkeleton from "@/app_modules/components/CustomSkeleton";
import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun";
import {
ComponentGlobal_NotifikasiBerhasil,
ComponentGlobal_NotifikasiGagal,
ComponentGlobal_NotifikasiPeringatan,
} from "@/app_modules/_global/notif_global";
import { ComponentAdminGlobal_NotifikasiPeringatan } from "../../_admin_global/admin_notifikasi/notifikasi_peringatan";
import { apiAdminCreateSticker } from "../lib/api_fetch_stiker";
interface IData {
name: string;
// jenis_kelamin: "Laki-laki" | "Perempuan" | null;
}
export default function AdminAppInformation_ViewCreateSticker() {
const [value, setValue] = useState(["senang"]);
const router = useRouter();
const [file, setFile] = useState<File | null>(null);
const [img, setImg] = useState<any | null>(null);
const [valueEmotion, setValueEmotion] = useState(["senang"]);
const [data, setData] = useState<IData>({
name: "",
// jenis_kelamin: null,
});
const listEmotion = [
{ value: "senang", label: "Senang" },
{ value: "sedih", label: "Sedih" },
{ value: "marah", label: "Marah" },
{ value: "takut", label: "Takut" },
{ value: "terkejut", label: "Terkejut" },
{ value: "cinta", label: "Cinta" },
{ value: "malas", label: "Malas" },
{ value: "bangga", label: "Bangga" },
{ value: "penasaran", label: "Penasaran" },
{ value: "malu", label: "Malu" },
{ value: "iri", label: "Iri" },
{ value: "kesal", label: "Kesal" },
{ value: "kaget", label: "Kaget" },
{ value: "bingung", label: "Bingung" },
{ value: "lega", label: "Lega" },
];
const [listEmotion, setListEmotion] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
useShallowEffect(() => {
onLoadMasterEmotions();
}, []);
async function onLoadMasterEmotions() {
try {
const response = await apiGetMasterEmotions();
if (response.success) {
setListEmotion(response.data);
}
} catch (error) {
console.error("Error on load master emotions:", error);
}
}
const validateData = () => {
if (!file) {
ComponentAdminGlobal_NotifikasiPeringatan("File tidak ada");
return false;
}
if (valueEmotion.length === 0) {
ComponentAdminGlobal_NotifikasiPeringatan("Pilih emosi");
return false;
}
return true;
};
async function onUploadFile() {
try {
const response = await funGlobal_UploadToStorage({
file: file as File,
dirId: DIRECTORY_ID.sticker,
});
if (!response.success) {
ComponentGlobal_NotifikasiPeringatan("Gagal upload gambar");
return;
}
return response.data.id;
} catch (error) {
console.error("Error on upload file", error);
}
}
async function handleCreateSticker({ fileId }: { fileId: string }) {
try {
const response = await apiAdminCreateSticker({
data: {
emotions: valueEmotion,
fileId: fileId,
},
});
if (response.success) {
ComponentGlobal_NotifikasiBerhasil("Berhasil disimpan");
router.back();
} else {
setLoading(false);
throw new Error("Failed to create sticker");
}
} catch (error) {
setLoading(false);
ComponentGlobal_NotifikasiGagal("Gagal disimpan");
console.error("Error create sticker", error);
}
}
async function onSubmit() {
if (!validateData()) return;
try {
setLoading(true);
const uploadFile = await onUploadFile();
if (!uploadFile) {
setLoading(false);
return;
}
await handleCreateSticker({ fileId: uploadFile });
} catch (error) {
console.error("Error on create sticker", error);
}
}
return (
<>
@@ -52,61 +157,124 @@ export default function AdminAppInformation_ViewCreateSticker() {
<Admin_ComponentBackButton />
<Admin_V3_ComponentBreakpoint lg={2} md={2} sm={1}>
<Admin_ComponentBoxStyle>
<Stack>
<Stack align="center">
<Paper bg={MainColor.white} p="sm" radius="lg">
<Image
alt="Preview Stiker"
src={pathAssetImage.dummy_image}
w="100%"
style={{ maxWidth: 300, objectFit: "contain" }}
radius="md"
/>
</Paper>
<Button radius="xl" leftIcon={<IconUpload size={20} />}>
Upload Stiker
</Button>
</Stack>
{!listEmotion.length ? (
<CustomSkeleton height={265} />
) : (
<Admin_ComponentBoxStyle>
<Stack>
<TextInput
<Stack spacing={"xs"}>
<ComponentGlobal_BoxUploadImage>
{img ? (
<AspectRatio ratio={1 / 1} mah={265} mx={"auto"}>
<Image
style={{
maxHeight: 250,
margin: "auto",
padding: "5px",
}}
alt="Foto"
height={250}
src={img}
/>
</AspectRatio>
) : (
<Stack
spacing={5}
justify="center"
align="center"
h={"100%"}
>
<IconPhoto size={100} />
</Stack>
)}
</ComponentGlobal_BoxUploadImage>
<Center>
<ComponentGlobal_ButtonUploadFileImage
accept="image/webp, image/jpeg, image/png"
onSetFile={setFile}
onSetImage={setImg}
/>
</Center>
</Stack>
<Stack>
{/* <TextInput
required
placeholder="Masukkan nama stiker"
label="Nama stiker"
styles={{
...baseStylesTextInput,
required: { color: MainColor.red },
}}
onChange={(val) => {
setData({
...data,
name: val.target.value,
});
}}
/> */}
{/* <Select
required
placeholder="Masukkan nama stiker"
label="Nama Stiker"
placeholder="Pilih jenis kelamin"
label="Jenis kelamin"
styles={{
...baseStylesTextInput,
required: { color: MainColor.red },
}}
/>
data={[
{ value: "Laki-laki", label: "Laki-laki" },
{ value: "Perempuan", label: "Perempuan" },
]}
onChange={(val: any) => {
setData({
...data,
jenis_kelamin: val,
});
}}
/> */}
<Stack>
<Component_V3_Label_TextInput text="Pilih emosi stiker" />
<Group style={{ display: "flex", flexWrap: "wrap" }}>
<Chip.Group multiple value={value} onChange={setValue}>
{listEmotion.map((e, i) => {
return (
<Chip key={i} value={e.value}>
{e.label}
</Chip>
);
})}
</Chip.Group>
</Group>
<Stack>
<Component_V3_Label_TextInput text="Pilih emosi stiker" />
<Group style={{ display: "flex", flexWrap: "wrap" }}>
<Chip.Group
multiple
value={valueEmotion}
onChange={setValueEmotion}
>
{listEmotion.map((e, i) => {
return (
<Chip key={i} value={e.value}>
{e.label}
</Chip>
);
})}
</Chip.Group>
</Group>
</Stack>
<Box
mt={"xl"}
style={{ display: "flex", justifyContent: "flex-end" }}
>
<Button
color="green"
bg={MainColor.green}
disabled={loading}
loading={loading}
loaderPosition="center"
radius="xl"
leftIcon={<IconCheck size={20} />}
onClick={() => onSubmit()}
>
Simpan
</Button>
</Box>
</Stack>
<Box
mt={"xl"}
style={{ display: "flex", justifyContent: "flex-end" }}
>
<Button radius="xl" leftIcon={<IconCheck size={20} />}>
Simpan
</Button>
</Box>
</Stack>
</Stack>
</Admin_ComponentBoxStyle>
</Admin_ComponentBoxStyle>
)}
</Admin_V3_ComponentBreakpoint>
</Stack>
</>

View File

@@ -1,72 +1,163 @@
"use client";
import { AdminColor } from "@/app_modules/_global/color/color_pallet";
import { Button, Center, ScrollArea, Stack, Table, Text } from "@mantine/core";
import { IconPlus } from "@tabler/icons-react";
import CustomSkeleton from "@/app_modules/components/CustomSkeleton";
import { APIs } from "@/lib";
import { RouterAdminAppInformation } from "@/lib/router_admin/router_app_information";
import {
Badge,
Box,
Button,
Center,
Group,
Image,
ScrollArea,
Spoiler,
Stack,
Table,
Text,
} from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { Prisma } from "@prisma/client";
import { IconPencil, IconPlus } from "@tabler/icons-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { ComponentAdminGlobal_TitlePage } from "../../_admin_global/_component";
import { Admin_ComponentBoxStyle } from "../../_admin_global/_component/comp_admin_boxstyle";
import { RouterAdminAppInformation } from "@/lib/router_admin/router_app_information";
import { apiAdminGetSticker } from "../lib/api_fetch_stiker";
import { ISticker } from "@/app_modules/_global/lib/interface/stiker";
export default function AdminAppInformation_ViewSticker() {
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const [dataSticker, setDataSticker] = useState<ISticker[] | null>(null);
useShallowEffect(() => {
const fetchData = async () => {
try {
const response = await apiAdminGetSticker();
if (response.success) {
setDataSticker(response.data);
}
} catch (error) {
console.error("Error fetching data", error);
}
};
fetchData();
}, []);
const rowTable = () => {
if (!Array.isArray(dataSticker) || dataSticker.length === 0) {
return (
<tr>
<td colSpan={12}>
<Center>
<Text color={"gray"}>Tidak ada data</Text>
</Center>
</td>
</tr>
);
}
return dataSticker.map((e, i) => (
<tr key={i}>
<td>
<Center>
<Button
radius={"xl"}
leftIcon={<IconPencil size={20} />}
onClick={() => {}}
>
Detail
</Button>
</Center>
</td>
<td>
<Center>
<Box bg="gray" p={"xs"}>
<Image
src={APIs.GET({ fileId: e.fileId, size: "200" })}
alt="Sticker"
width={100}
height={100}
/>
</Box>
</Center>
</td>
<td>
<Center>
<Box maw={300}>
<Spoiler
maxHeight={50}
hideLabel="Sembunyikan"
showLabel="Tampilkan"
>
<Group>
{e.MasterEmotions.map((e) => (
<Badge key={e.value}>{e.value}</Badge>
))}
</Group>
</Spoiler>
</Box>
</Center>
</td>
</tr>
));
};
return (
<>
<Stack>
<ComponentAdminGlobal_TitlePage name="Stiker " />
<Button
loading={isLoading}
loaderPosition="center"
w={120}
radius={"xl"}
leftIcon={<IconPlus size={20} />}
onClick={() => {
router.push(RouterAdminAppInformation.createSticker);
setIsLoading(true);
}}
>
Tambah
</Button>
<Admin_ComponentBoxStyle
style={{ height: "65dvh", overflow: "hidden" }}
>
<ScrollArea w={"100%"} h={"100%"} scrollbarSize={"md"}>
<Table
verticalSpacing={"md"}
horizontalSpacing={"md"}
p={"md"}
w={"100%"}
>
<thead>
<tr>
<th>
<Center c={AdminColor.white}>Aksi</Center>
</th>
<th>
<Center c={AdminColor.white}>Status</Center>
</th>
<th>
<Text c={AdminColor.white}>Kategori</Text>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Stiker 1</td>
<td>
<Button
radius={"xl"}
leftIcon={<IconPlus size={20} />}
onClick={() => {}}
>
Tambah
</Button>
</td>
</tr>
</tbody>
</Table>
</ScrollArea>
</Admin_ComponentBoxStyle>
{!dataSticker ? (
<CustomSkeleton height={"65dvh"} />
) : (
<Admin_ComponentBoxStyle
style={{ height: "65dvh", overflow: "hidden" }}
>
<ScrollArea w={"100%"} h={"100%"} scrollbarSize={"md"}>
<Table
verticalSpacing={"md"}
horizontalSpacing={"md"}
p={"md"}
w={"100%"}
>
<thead>
<tr>
<th>
<Center c={AdminColor.white}>Aksi</Center>
</th>
<th>
<Center c={AdminColor.white}>Stiker</Center>
</th>
<th>
<Center c={AdminColor.white}>Kategori</Center>
</th>
</tr>
</thead>
<tbody>{rowTable()}</tbody>
</Table>
</ScrollArea>
</Admin_ComponentBoxStyle>
)}
</Stack>
</>
);

View File

@@ -2,9 +2,11 @@ import master_kategori_app from "./master_kategori_app.json";
import master_nama_bank from "./master_nama_bank.json";
import master_status_transaksi from "./master_status_transaksi.json";
import master_new_bidang_bisnis from "./master_new_bidang_bisnis.json";
import master_emotions from "./master_emotions.json";
export { master_kategori_app };
export { master_nama_bank };
export { master_status_transaksi };
export { master_new_bidang_bisnis };
export { master_emotions };

View File

@@ -0,0 +1,17 @@
[
{ "value": "senang", "label": "Senang" },
{ "value": "sedih", "label": "Sedih" },
{ "value": "marah", "label": "Marah" },
{ "value": "takut", "label": "Takut" },
{ "value": "terkejut", "label": "Terkejut" },
{ "value": "cinta", "label": "Cinta" },
{ "value": "malas", "label": "Malas" },
{ "value": "bangga", "label": "Bangga" },
{ "value": "penasaran", "label": "Penasaran" },
{ "value": "malu", "label": "Malu" },
{ "value": "iri", "label": "Iri" },
{ "value": "kesal", "label": "Kesal" },
{ "value": "kaget", "label": "Kaget" },
{ "value": "bingung", "label": "Bingung" },
{ "value": "lega", "label": "Lega" }
]

View File

@@ -25,6 +25,9 @@ const DIRECTORY_ID = {
// Event
event_sponsor: "cm65zlbyf001udvmggnd6i0oh",
event_bukti_transfer: "cm65zlehy001wdvmgnobur2zh",
// Sticker
sticker: "cmanquv32002fcesbk49cj07g",
};
export default DIRECTORY_ID;