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 # 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" provider = "postgresql"

View File

@@ -152,8 +152,6 @@ model Portofolio_MediaSosial {
portofolioId String? @unique portofolioId String? @unique
} }
// ------------------- MASTER -------------------------- //
model Portofolio_BidangDanSubBidangBisnis { model Portofolio_BidangDanSubBidangBisnis {
id String @id @default(cuid()) id String @id @default(cuid())
isActive Boolean @default(true) isActive Boolean @default(true)
@@ -168,6 +166,8 @@ model Portofolio_BidangDanSubBidangBisnis {
masterSubBidangBisnisId String? masterSubBidangBisnisId String?
} }
// ------------------- MASTER -------------------------- //
model MasterBidangBisnis { model MasterBidangBisnis {
id String @id @default(uuid()) id String @id @default(uuid())
name String name String
@@ -222,6 +222,16 @@ model MasterStatusTransaksi {
EventTransaksi EventTransaksi[] 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 --------------------- // // -------------------- INVESTASI --------------------- //
// Table investasi / saham // Table investasi / saham
model Investasi { model Investasi {
@@ -1046,3 +1056,16 @@ model EventTransaksi {
MasterStatusTransaksi MasterStatusTransaksi? @relation(fields: [masterStatusTransaksiId], references: [id]) MasterStatusTransaksi MasterStatusTransaksi? @relation(fields: [masterStatusTransaksiId], references: [id])
masterStatusTransaksiId String? 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 { master_status_transaksi } from "@/bin/seeder/master";
import pLimit from "p-limit"; import pLimit from "p-limit";
import { master_new_bidang_bisnis } from "@/bin/seeder/master"; import { master_new_bidang_bisnis } from "@/bin/seeder/master";
import { master_emotions } from "@/bin/seeder/master";
async function masterUserRole() { async function masterUserRole() {
for (let i of userRole) { for (let i of userRole) {
@@ -614,6 +615,20 @@ async function masterStatusTransaksi() {
console.log("masterStatusTransaksi success"); 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 = [ const listSeederQueue = [
masterUserRole, masterUserRole,
seederUser, seederUser,
@@ -643,6 +658,7 @@ const listSeederQueue = [
masterKategoriApp, masterKategoriApp,
masterInvestasiNewTransaksiStatus, masterInvestasiNewTransaksiStatus,
masterStatusTransaksi, masterStatusTransaksi,
masterEmotions,
]; ];
const limit = pLimit(1); const limit = pLimit(1);

View File

@@ -3,6 +3,7 @@ export {
apiGetMasterBidangBisnis, apiGetMasterBidangBisnis,
apiGetMasterStatusTransaksi, apiGetMasterStatusTransaksi,
apiGetAdminContact, apiGetAdminContact,
apiGetMasterEmotions,
}; };
const apiGetMasterBank = async () => { const apiGetMasterBank = async () => {
@@ -90,3 +91,20 @@ const apiGetAdminContact = async () => {
throw error; // Re-throw the error to handle it in the calling function 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"; "use client";
import { import {
AspectRatio,
Box, Box,
Button, Button,
Center,
Chip, Chip,
Group, Group,
Image, Image,
Paper, Paper,
Select,
Stack, Stack,
TextInput, TextInput,
} from "@mantine/core"; } from "@mantine/core";
import { ComponentAdminGlobal_TitlePage } from "../../_admin_global/_component"; import { ComponentAdminGlobal_TitlePage } from "../../_admin_global/_component";
import { Admin_ComponentBoxStyle } from "../../_admin_global/_component/comp_admin_boxstyle"; import { Admin_ComponentBoxStyle } from "../../_admin_global/_component/comp_admin_boxstyle";
import { Admin_V3_ComponentBreakpoint } from "../../_components_v3/comp_simple_grid_breakpoint"; 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 Admin_ComponentBackButton from "../../_admin_global/back_button";
import { IconCheck, IconUpload } from "@tabler/icons-react"; import { IconCheck, IconPhoto, IconUpload } from "@tabler/icons-react";
import { import {
AdminColor, AdminColor,
MainColor, MainColor,
@@ -23,27 +26,129 @@ import {
import { baseStylesTextInput } from "@/app_modules/_global/lib/base_style_text_input"; import { baseStylesTextInput } from "@/app_modules/_global/lib/base_style_text_input";
import { useState } from "react"; import { useState } from "react";
import Component_V3_Label_TextInput from "@/app_modules/_global/component/new/comp_V3_label_text_input"; 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() { 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 = [ const [listEmotion, setListEmotion] = useState<any[]>([]);
{ value: "senang", label: "Senang" }, const [loading, setLoading] = useState(false);
{ value: "sedih", label: "Sedih" },
{ value: "marah", label: "Marah" }, useShallowEffect(() => {
{ value: "takut", label: "Takut" }, onLoadMasterEmotions();
{ value: "terkejut", label: "Terkejut" }, }, []);
{ value: "cinta", label: "Cinta" },
{ value: "malas", label: "Malas" }, async function onLoadMasterEmotions() {
{ value: "bangga", label: "Bangga" }, try {
{ value: "penasaran", label: "Penasaran" }, const response = await apiGetMasterEmotions();
{ value: "malu", label: "Malu" },
{ value: "iri", label: "Iri" }, if (response.success) {
{ value: "kesal", label: "Kesal" }, setListEmotion(response.data);
{ value: "kaget", label: "Kaget" }, }
{ value: "bingung", label: "Bingung" }, } catch (error) {
{ value: "lega", label: "Lega" }, 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 ( return (
<> <>
@@ -52,61 +157,124 @@ export default function AdminAppInformation_ViewCreateSticker() {
<Admin_ComponentBackButton /> <Admin_ComponentBackButton />
<Admin_V3_ComponentBreakpoint lg={2} md={2} sm={1}> <Admin_V3_ComponentBreakpoint lg={2} md={2} sm={1}>
<Admin_ComponentBoxStyle> {!listEmotion.length ? (
<Stack> <CustomSkeleton height={265} />
<Stack align="center"> ) : (
<Paper bg={MainColor.white} p="sm" radius="lg"> <Admin_ComponentBoxStyle>
<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>
<Stack> <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 required
placeholder="Masukkan nama stiker" placeholder="Pilih jenis kelamin"
label="Nama Stiker" label="Jenis kelamin"
styles={{ styles={{
...baseStylesTextInput, ...baseStylesTextInput,
required: { color: MainColor.red }, required: { color: MainColor.red },
}} }}
/> data={[
{ value: "Laki-laki", label: "Laki-laki" },
{ value: "Perempuan", label: "Perempuan" },
]}
onChange={(val: any) => {
setData({
...data,
jenis_kelamin: val,
});
}}
/> */}
<Stack> <Stack>
<Component_V3_Label_TextInput text="Pilih emosi stiker" /> <Component_V3_Label_TextInput text="Pilih emosi stiker" />
<Group style={{ display: "flex", flexWrap: "wrap" }}> <Group style={{ display: "flex", flexWrap: "wrap" }}>
<Chip.Group multiple value={value} onChange={setValue}> <Chip.Group
{listEmotion.map((e, i) => { multiple
return ( value={valueEmotion}
<Chip key={i} value={e.value}> onChange={setValueEmotion}
{e.label} >
</Chip> {listEmotion.map((e, i) => {
); return (
})} <Chip key={i} value={e.value}>
</Chip.Group> {e.label}
</Group> </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> </Stack>
<Box
mt={"xl"}
style={{ display: "flex", justifyContent: "flex-end" }}
>
<Button radius="xl" leftIcon={<IconCheck size={20} />}>
Simpan
</Button>
</Box>
</Stack> </Stack>
</Stack> </Admin_ComponentBoxStyle>
</Admin_ComponentBoxStyle> )}
</Admin_V3_ComponentBreakpoint> </Admin_V3_ComponentBreakpoint>
</Stack> </Stack>
</> </>

View File

@@ -1,72 +1,163 @@
"use client"; "use client";
import { AdminColor } from "@/app_modules/_global/color/color_pallet"; import { AdminColor } from "@/app_modules/_global/color/color_pallet";
import { Button, Center, ScrollArea, Stack, Table, Text } from "@mantine/core"; import CustomSkeleton from "@/app_modules/components/CustomSkeleton";
import { IconPlus } from "@tabler/icons-react"; 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 { useRouter } from "next/navigation";
import { useState } from "react";
import { ComponentAdminGlobal_TitlePage } from "../../_admin_global/_component"; import { ComponentAdminGlobal_TitlePage } from "../../_admin_global/_component";
import { Admin_ComponentBoxStyle } from "../../_admin_global/_component/comp_admin_boxstyle"; 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() { export default function AdminAppInformation_ViewSticker() {
const router = useRouter(); 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 ( return (
<> <>
<Stack> <Stack>
<ComponentAdminGlobal_TitlePage name="Stiker " /> <ComponentAdminGlobal_TitlePage name="Stiker " />
<Button <Button
loading={isLoading}
loaderPosition="center"
w={120} w={120}
radius={"xl"} radius={"xl"}
leftIcon={<IconPlus size={20} />} leftIcon={<IconPlus size={20} />}
onClick={() => { onClick={() => {
router.push(RouterAdminAppInformation.createSticker); router.push(RouterAdminAppInformation.createSticker);
setIsLoading(true);
}} }}
> >
Tambah Tambah
</Button> </Button>
<Admin_ComponentBoxStyle {!dataSticker ? (
style={{ height: "65dvh", overflow: "hidden" }} <CustomSkeleton height={"65dvh"} />
> ) : (
<ScrollArea w={"100%"} h={"100%"} scrollbarSize={"md"}> <Admin_ComponentBoxStyle
<Table style={{ height: "65dvh", overflow: "hidden" }}
verticalSpacing={"md"} >
horizontalSpacing={"md"} <ScrollArea w={"100%"} h={"100%"} scrollbarSize={"md"}>
p={"md"} <Table
w={"100%"} verticalSpacing={"md"}
> horizontalSpacing={"md"}
<thead> p={"md"}
<tr> w={"100%"}
<th> >
<Center c={AdminColor.white}>Aksi</Center> <thead>
</th> <tr>
<th> <th>
<Center c={AdminColor.white}>Status</Center> <Center c={AdminColor.white}>Aksi</Center>
</th> </th>
<th> <th>
<Text c={AdminColor.white}>Kategori</Text> <Center c={AdminColor.white}>Stiker</Center>
</th> </th>
</tr> <th>
</thead> <Center c={AdminColor.white}>Kategori</Center>
<tbody> </th>
<tr> </tr>
<td>1</td> </thead>
<td>Stiker 1</td> <tbody>{rowTable()}</tbody>
<td> </Table>
<Button </ScrollArea>
radius={"xl"} </Admin_ComponentBoxStyle>
leftIcon={<IconPlus size={20} />} )}
onClick={() => {}}
>
Tambah
</Button>
</td>
</tr>
</tbody>
</Table>
</ScrollArea>
</Admin_ComponentBoxStyle>
</Stack> </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_nama_bank from "./master_nama_bank.json";
import master_status_transaksi from "./master_status_transaksi.json"; import master_status_transaksi from "./master_status_transaksi.json";
import master_new_bidang_bisnis from "./master_new_bidang_bisnis.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_kategori_app };
export { master_nama_bank }; export { master_nama_bank };
export { master_status_transaksi }; export { master_status_transaksi };
export { master_new_bidang_bisnis }; 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
event_sponsor: "cm65zlbyf001udvmggnd6i0oh", event_sponsor: "cm65zlbyf001udvmggnd6i0oh",
event_bukti_transfer: "cm65zlehy001wdvmgnobur2zh", event_bukti_transfer: "cm65zlehy001wdvmgnobur2zh",
// Sticker
sticker: "cmanquv32002fcesbk49cj07g",
}; };
export default DIRECTORY_ID; export default DIRECTORY_ID;