From bd2016deb3310d77ea52e7c9635724038ef38291 Mon Sep 17 00:00:00 2001 From: amel Date: Thu, 12 Dec 2024 17:21:31 +0800 Subject: [PATCH 01/23] upd: donasi Deskripsi: - update api master untuk create donasi No Issues --- src/app/api/new/donasi/master/route.ts | 33 +++ .../dev/donasi/create/create_donasi/page.tsx | 14 +- .../create/create_cerita_penggalang.tsx | 2 +- .../donasi/create/create_donasi_new.tsx | 264 ++++++++++++++++++ src/app_modules/donasi/index.ts | 2 + src/app_modules/donasi/lib/api_donasi.ts | 5 + 6 files changed, 311 insertions(+), 9 deletions(-) create mode 100644 src/app/api/new/donasi/master/route.ts create mode 100644 src/app_modules/donasi/create/create_donasi_new.tsx diff --git a/src/app/api/new/donasi/master/route.ts b/src/app/api/new/donasi/master/route.ts new file mode 100644 index 00000000..f42e4ab5 --- /dev/null +++ b/src/app/api/new/donasi/master/route.ts @@ -0,0 +1,33 @@ +import { prisma } from "@/app/lib"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + +// GET ALL DATA MASTER UNTUK DONASI +export async function GET(request: Request) { + try { + let dataFix + const { searchParams } = new URL(request.url) + const kategori = searchParams.get("cat") + + if (kategori == "kategori") { + dataFix = await prisma.donasiMaster_Kategori.findMany({ + orderBy: { + createdAt: "asc", + }, + where: { + active: true, + } + }) + } else if (kategori == "durasi") { + dataFix = await prisma.donasiMaster_Durasi.findMany() + } + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti ", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/dev/donasi/create/create_donasi/page.tsx b/src/app/dev/donasi/create/create_donasi/page.tsx index 2202dfdf..aef24090 100644 --- a/src/app/dev/donasi/create/create_donasi/page.tsx +++ b/src/app/dev/donasi/create/create_donasi/page.tsx @@ -1,14 +1,12 @@ -import { CreateDonasi } from "@/app_modules/donasi"; -import { - Donasi_getMasterDurasi, - Donasi_getMasterKategori, -} from "@/app_modules/donasi/fun"; +import { CreateDonasiNew } from "@/app_modules/donasi"; + export default async function Page() { - const masterKategori = await Donasi_getMasterKategori(); - const masterDurasi = await Donasi_getMasterDurasi(); + // const masterKategori = await Donasi_getMasterKategori(); + // const masterDurasi = await Donasi_getMasterDurasi(); return ( - + // + ); } diff --git a/src/app_modules/donasi/create/create_cerita_penggalang.tsx b/src/app_modules/donasi/create/create_cerita_penggalang.tsx index 046a4cef..ae5defbd 100644 --- a/src/app_modules/donasi/create/create_cerita_penggalang.tsx +++ b/src/app_modules/donasi/create/create_cerita_penggalang.tsx @@ -272,7 +272,7 @@ export default function CreateCeritaPenggalangDonasi({ }, }} withAsterisk - placeholder="Maskuan nomor rekening" + placeholder="Masukan nomor rekening" label="Nomor rekening" maxLength={100} onChange={(val) => { diff --git a/src/app_modules/donasi/create/create_donasi_new.tsx b/src/app_modules/donasi/create/create_donasi_new.tsx new file mode 100644 index 00000000..85389da1 --- /dev/null +++ b/src/app_modules/donasi/create/create_donasi_new.tsx @@ -0,0 +1,264 @@ +"use client"; +import { DIRECTORY_ID } from "@/app/lib"; +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { MainColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_BoxUploadImage } from "@/app_modules/_global/component"; +import ComponentGlobal_BoxInformation from "@/app_modules/_global/component/box_information"; +import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; +import { AspectRatio, Button, FileButton, Group, Image, Select, Stack, Text, TextInput, } from "@mantine/core"; +import { IconCamera, IconUpload } from "@tabler/icons-react"; +import { useAtom } from "jotai"; +import _ from "lodash"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import Donasi_funCreateTemporary from "../fun/create/fun_create_donasi_temporary"; +import { gs_donasi_tabs_posting } from "../global_state"; +import { apiGetMasterDonasi } from "../lib/api_donasi"; +import { useShallowEffect } from "@mantine/hooks"; + +export default function CreateDonasiNew() { + const router = useRouter(); + const [loadingMaster, setLoadingMaster] = useState(true) + const [isLoading, setLoading] = useState(false); + const [kategori, setKategori] = useState([]); + const [durasi, setDurasi] = useState([]); + const [data, setData] = useState({ + kategoriId: "", + title: "", + target: "", + durasiId: "", + }); + const [targetDana, setTargetDana] = useState(""); + const [file, setFile] = useState(null); + const [img, setImg] = useState(); + const [tabsPostingDonasi, setTabsPostingDonasi] = useAtom( + gs_donasi_tabs_posting + ); + + async function onGetMaster() { + try { + setLoadingMaster(true) + const responseKategori = await apiGetMasterDonasi("?cat=kategori") + const responseDurasi = await apiGetMasterDonasi("?cat=durasi") + if (responseKategori.success) { + setKategori(responseKategori.data) + } + if (responseDurasi.success) { + setDurasi(responseDurasi.data) + } + } catch (error) { + console.log(error); + } finally { + setLoadingMaster(false) + } + } + + useShallowEffect(() => { + onGetMaster() + }, []) + + async function onCreate() { + const body = { + donasiMaster_KategoriId: data.kategoriId, + donasiMaster_DurasiId: data.durasiId, + title: data.title, + target: targetDana, + }; + + if (_.values(body).includes("")) + return ComponentGlobal_NotifikasiPeringatan("Lengkapin Data"); + + try { + setLoading(true); + + const uploadImage = await funGlobal_UploadToStorage({ + file: file as File, + dirId: DIRECTORY_ID.donasi_image, + }); + if (!uploadImage.success) { + setLoading(false); + return ComponentGlobal_NotifikasiPeringatan("Gagal upload file gambar"); + } + + const res = await Donasi_funCreateTemporary({ + data: body as any, + fileId: uploadImage.data.id, + }); + if (res.status === 201) { + setTabsPostingDonasi("Review"); + router.push(RouterDonasi.create_cerita_penggalang + `${res.donasiId}`); + } else { + ComponentGlobal_NotifikasiGagal(res.message); + setLoading(false); + } + } catch (error) { + console.log(error); + } + } + + return ( + <> + + + ({ + value: e.id, + label: e.name + " " + `hari`, + }))} + onChange={(val: string) => setData({ ...data, durasiId: val })} + /> + + + + + {img ? ( + + Foto + + ) : ( + + + + Upload Gambar + + + )} + + + {/* Upload Foto */} + + { + try { + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + setImg(buffer); + setFile(files); + } catch (error) { + console.log(error); + } + }} + accept="image/png,image/jpeg" + > + {(props) => ( + + )} + + + + + + + + ); +} diff --git a/src/app_modules/donasi/index.ts b/src/app_modules/donasi/index.ts index 62da8ec0..f5d776c2 100644 --- a/src/app_modules/donasi/index.ts +++ b/src/app_modules/donasi/index.ts @@ -5,6 +5,7 @@ import GalangDanaDonasi from "./main/galang_dana/ui_galang_dana"; import GalangDanaDonasiNew from "./main/galang_dana/ui_galang_dana_new"; import DonasiSayaDonasi from "./main/donasi_saya"; import CreateDonasi from "./create/create_donasi"; +import CreateDonasiNew from "./create/create_donasi_new"; import LayoutCreateDonasi from "./create/layout"; import DetailMainDonasi from "./detail/detail_main"; import LayoutDetailMainDonasi from "./detail/detail_main/layout"; @@ -103,4 +104,5 @@ export { LayoutDonasi_EditRekening, MainDonasiNew, GalangDanaDonasiNew, + CreateDonasiNew }; diff --git a/src/app_modules/donasi/lib/api_donasi.ts b/src/app_modules/donasi/lib/api_donasi.ts index 36e2f5f6..04f661f3 100644 --- a/src/app_modules/donasi/lib/api_donasi.ts +++ b/src/app_modules/donasi/lib/api_donasi.ts @@ -1,4 +1,9 @@ export const apiGetAllDonasi = async (path?: string) => { const response = await fetch(`/api/new/donasi${(path) ? path : ''}`) return await response.json().catch(() => null) +} + +export const apiGetMasterDonasi = async (path?: string) => { + const response = await fetch(`/api/new/donasi/master${(path) ? path : ''}`) + return await response.json().catch(() => null) } \ No newline at end of file From 7cbbcd037038b4522f599c3b5e9693ad9e78b8ae Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Fri, 13 Dec 2024 07:37:31 +0800 Subject: [PATCH 02/23] Fix upload image profile and portofolio --- src/app/api/job/get-all/route.ts | 100 ++++- src/app/page.tsx | 4 +- .../button/comp_button_upload_photo.tsx | 1 + .../fun/upload/fun_upload_to_storage.ts | 2 + .../_global/ui/ui_image_preview.tsx | 6 +- src/app_modules/home/component/body_home.tsx | 349 +++++++++--------- .../home/component/footer_home.tsx | 200 +++++----- src/app_modules/home/view_home_new.tsx | 202 +++++----- .../job/main/beranda/ui_beranda.tsx | 124 ------- .../job/main/beranda/view_beranda.tsx | 118 +++++- .../button/comp_button_selanjutnya.tsx | 52 ++- .../katalog/portofolio/create/view.tsx | 63 +++- .../button/comp_create_new_profile.tsx | 25 -- .../katalog/profile/create/view.tsx | 65 +++- .../button/comp_button_save_pin.tsx | 56 ++- src/app_modules/map/ui/ui_create_pin.tsx | 65 +++- 16 files changed, 824 insertions(+), 608 deletions(-) delete mode 100644 src/app_modules/job/main/beranda/ui_beranda.tsx diff --git a/src/app/api/job/get-all/route.ts b/src/app/api/job/get-all/route.ts index 21db076c..1601d2bb 100644 --- a/src/app/api/job/get-all/route.ts +++ b/src/app/api/job/get-all/route.ts @@ -1,16 +1,94 @@ -import { job_getAllListPublish } from "@/app_modules/job/fun/get/get_all_publish"; -import _ from "lodash"; +import { prisma } from "@/app/lib"; import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; -export async function GET(params: Request) { - const { searchParams } = new URL(params.url); - const page = searchParams.get("page"); - const search = searchParams.get("search"); +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const search = searchParams.get("search"); + const page = searchParams.get("page"); + const dataTake = 10; + const dataSkip = Number(page) * dataTake - dataTake; - const data = await job_getAllListPublish({ - page: _.toNumber(page), - search: search as string, - }); + if (search != "") { + const data = await prisma.job.findMany({ + take: dataTake, + skip: dataSkip, + orderBy: { + updatedAt: "desc", + }, + where: { + masterStatusId: "1", + isActive: true, + isArsip: false, + title: { + mode: "insensitive", + contains: search as string, + }, + }, + select: { + id: true, + title: true, + Author: { + select: { + id: true, + username: true, + Profile: true, + }, + }, + }, + }); - return NextResponse.json({ data }); + return NextResponse.json( + { + success: true, + message: "Berhasil ambil data", + data: data, + }, + { status: 200 } + ); + } else { + const data = await prisma.job.findMany({ + take: dataTake, + skip: dataSkip, + orderBy: { + updatedAt: "desc", + }, + where: { + masterStatusId: "1", + isActive: true, + isArsip: false, + title: { + mode: "insensitive", + }, + }, + select: { + id: true, + title: true, + Author: { + select: { + id: true, + username: true, + Profile: true, + }, + }, + }, + }); + + return NextResponse.json( + { + success: true, + message: "Berhasil ambil data", + data: data, + }, + { status: 200 } + ); + } + } catch (error) { + console.error(error); + return NextResponse.json({ + success: false, + message: "Gagal ambil data", + }); + } } diff --git a/src/app/page.tsx b/src/app/page.tsx index 3dc0d2d4..6fc3a06d 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,11 +5,11 @@ import { useShallowEffect } from "@mantine/hooks"; import { useRouter } from "next/navigation"; export default function Page() { - const router = useRouter() + const router = useRouter(); useShallowEffect(() => { setTimeout(() => { // window.location.replace("/dev/home"); - router.replace("/dev/home"); + router.replace("/dev/home", { scroll: false }); }, 1000); }, []); diff --git a/src/app_modules/_global/button/comp_button_upload_photo.tsx b/src/app_modules/_global/button/comp_button_upload_photo.tsx index 0a28c023..8e56d342 100644 --- a/src/app_modules/_global/button/comp_button_upload_photo.tsx +++ b/src/app_modules/_global/button/comp_button_upload_photo.tsx @@ -24,6 +24,7 @@ export function ComponentGlobal_ButtonUploadFileImage({ if (files.size > MAX_SIZE) { ComponentGlobal_NotifikasiPeringatan(PemberitahuanMaksimalFile); + return; } else { onSetFile(files); onSetImage(buffer); diff --git a/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts b/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts index 06ed38f4..df5b7377 100644 --- a/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts +++ b/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts @@ -54,6 +54,8 @@ export async function funGlobal_UploadToStorage({ if (res.ok) { const dataRes = await res.json(); + // const cekLog = await res.text(); + // console.log(cekLog); return { success: true, data: dataRes.data }; } else { const errorText = await res.text(); diff --git a/src/app_modules/_global/ui/ui_image_preview.tsx b/src/app_modules/_global/ui/ui_image_preview.tsx index a2057b2d..fce91d58 100644 --- a/src/app_modules/_global/ui/ui_image_preview.tsx +++ b/src/app_modules/_global/ui/ui_image_preview.tsx @@ -10,18 +10,16 @@ import { Image, rem, ScrollArea, - Skeleton, - Text, - Title, + Skeleton } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { IconX } from "@tabler/icons-react"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { MainColor } from "../color"; +import ComponentGlobal_Loader from "../component/loader"; import UIGlobal_LayoutHeaderTamplate from "./ui_header_tamplate"; import { UIHeader } from "./ui_layout_tamplate"; -import ComponentGlobal_Loader from "../component/loader"; export function UIGlobal_ImagePreview({ fileId }: { fileId: string }) { const router = useRouter(); diff --git a/src/app_modules/home/component/body_home.tsx b/src/app_modules/home/component/body_home.tsx index d5984b8d..69f7dac2 100644 --- a/src/app_modules/home/component/body_home.tsx +++ b/src/app_modules/home/component/body_home.tsx @@ -1,7 +1,17 @@ import { AccentColor, MainColor } from "@/app_modules/_global/color"; import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; -import { ActionIcon, Box, Group, Image, Paper, SimpleGrid, Skeleton, Stack, Text } from "@mantine/core"; +import { + ActionIcon, + Box, + Group, + Image, + Paper, + SimpleGrid, + Skeleton, + Stack, + Text, +} from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { IconUserSearch } from "@tabler/icons-react"; import _ from "lodash"; @@ -9,185 +19,182 @@ import { useRouter } from "next/navigation"; import { useState } from "react"; import { apiGetDataHome } from "../fun/get/api_home"; import { listMenuHomeBody, menuHomeJob } from "./list_menu_home"; +import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; export default function BodyHome() { - const router = useRouter() - const [dataUser, setDataUser] = useState({}) - const [dataJob, setDataJob] = useState([]) - const [loadingJob, setLoadingJob] = useState(true) + const router = useRouter(); + const [dataUser, setDataUser] = useState({}); + const [dataJob, setDataJob] = useState([]); + const [loadingJob, setLoadingJob] = useState(true); - useShallowEffect(() => { - cekUserLogin() - getHomeJob() - }, []); + useShallowEffect(() => { + cekUserLogin(); + getHomeJob(); + }, []); - - async function cekUserLogin() { - try { - const response = await apiGetDataHome("?cat=cek_profile") - if (response.success) { - setDataUser(response.data); - } - } catch (error) { - console.error(error); + async function cekUserLogin() { + try { + const response = await apiGetDataHome("?cat=cek_profile"); + if (response.success) { + setDataUser(response.data); } - } + } catch (error) { + console.error(error); + } + } - async function getHomeJob() { - try { - setLoadingJob(true) - const response = await apiGetDataHome("?cat=job") - if (response.success) { - setDataJob(response.data); - } - } catch (error) { - console.error(error); - } finally { - setLoadingJob(false) + async function getHomeJob() { + try { + setLoadingJob(true); + const response = await apiGetDataHome("?cat=job"); + if (response.success) { + setDataJob(response.data); } - } + } catch (error) { + console.error(error); + } finally { + setLoadingJob(false); + } + } - return ( - - - logo - + return ( + + + logo + - - - {listMenuHomeBody.map((e, i) => ( - { - if (dataUser.profile === undefined || dataUser?.profile === null) { - return ComponentGlobal_NotifikasiPeringatan( - "Lengkapi Profile" - ); - } else { - if (e.link === "") { - return ComponentGlobal_NotifikasiPeringatan( - "Cooming Soon !!" - ); - } else { - router.push(e.link, { scroll: false }); - } - } - }} - > - - - {e.icon} - - - {e.name} - - - - ))} - - - {/* Job View */} + + + {listMenuHomeBody.map((e, i) => ( - { - if (dataUser.profile === undefined || dataUser?.profile === null) { - return ComponentGlobal_NotifikasiPeringatan( - "Lengkapi Profile" - ); - } else { - if (menuHomeJob.link === "") { - return ComponentGlobal_NotifikasiPeringatan( - "Cooming Soon !!" - ); - } else { - return router.push(menuHomeJob.link, { scroll: false }); - } - } - }} - > - - - {menuHomeJob.icon} - - - {menuHomeJob.name} - - - { - loadingJob ? - Array(2) - .fill(null) - .map((_, i) => ( - - - - - - )) - : _.isEmpty(dataJob) ? - () - : ( - - {dataJob.map((e, i) => ( - - - - - - - - {e?.Author.username} - - - {e?.title} - - - - - ))} - - ) + key={e.id} + h={150} + bg={MainColor.darkblue} + style={{ + borderRadius: "10px 10px 10px 10px", + border: `2px solid ${AccentColor.blue}`, + }} + onClick={() => { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + if (e.link === "") { + return ComponentGlobal_NotifikasiPeringatan( + "Cooming Soon !!" + ); + } else { + router.push(e.link, { scroll: false }); } - + } + }} + > + + + {e.icon} + + + {e.name} + + - - - ); -} \ No newline at end of file + ))} + + + {/* Job View */} + + { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + if (menuHomeJob.link === "") { + return ComponentGlobal_NotifikasiPeringatan( + "Cooming Soon !!" + ); + } else { + return router.push(menuHomeJob.link, { scroll: false }); + } + } + }} + > + + + {menuHomeJob.icon} + + + {menuHomeJob.name} + + + {loadingJob ? ( + Array(2) + .fill(null) + .map((_, i) => ( + + + + + + )) + ) : _.isEmpty(dataJob) ? ( + + ) : ( + + {dataJob.map((e, i) => ( + + + + + + + + {e?.Author.username} + + + {e?.title} + + + + + ))} + + )} + + + + + ); +} diff --git a/src/app_modules/home/component/footer_home.tsx b/src/app_modules/home/component/footer_home.tsx index f116b19d..e1503ffa 100644 --- a/src/app_modules/home/component/footer_home.tsx +++ b/src/app_modules/home/component/footer_home.tsx @@ -1,7 +1,14 @@ import { APIs } from "@/app/lib"; import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; -import { ActionIcon, Box, Center, SimpleGrid, Stack, Text } from "@mantine/core"; +import { + ActionIcon, + Box, + Center, + SimpleGrid, + Stack, + Text, +} from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { IconUserCircle } from "@tabler/icons-react"; import { useRouter } from "next/navigation"; @@ -11,103 +18,104 @@ import { Home_ComponentAvatarProfile } from "./comp_avatar_profile"; import { listMenuHomeFooter } from "./list_menu_home"; export default function FooterHome() { - const router = useRouter() - const [dataUser, setDataUser] = useState({}) + const router = useRouter(); + const [dataUser, setDataUser] = useState({}); - useShallowEffect(() => { - cekUserLogin(); - }, []); + useShallowEffect(() => { + cekUserLogin(); + }, []); - - async function cekUserLogin() { - try { - const response = await apiGetDataHome("?cat=cek_profile") - if (response.success) { - setDataUser(response.data); - } - } catch (error) { - console.error(error); + async function cekUserLogin() { + try { + const response = await apiGetDataHome("?cat=cek_profile"); + if (response.success) { + setDataUser(response.data); } - } + } catch (error) { + console.error(error); + } + } - return ( - - - {listMenuHomeFooter.map((e) => ( -
- { - if (dataUser.profile === undefined || dataUser?.profile === null) { - ComponentGlobal_NotifikasiPeringatan("Lengkapi Profile"); - } else { - if (e.link == "") { - ComponentGlobal_NotifikasiPeringatan("Cooming Soon") - } else { - router.push(e.link, { scroll: false }) - } - } - }} - > - - {e.icon} - - - {e.name} - - -
- ))} + return ( + + + {listMenuHomeFooter.map((e) => ( +
+ { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + if (e.link == "") { + ComponentGlobal_NotifikasiPeringatan("Cooming Soon"); + } else { + router.push(e.link, { scroll: false }); + } + } + }} + > + + {e.icon} + + + {e.name} + + +
+ ))} -
- { - if (dataUser.profile === undefined || dataUser?.profile === null) { - router.push(RouterProfile.create, { scroll: false }); - } else { - router.push( - RouterProfile.katalogOLD + `${dataUser?.profile}`, - { scroll: false } - ); - } - }} - > - - {dataUser.profile === undefined || dataUser?.profile === null - ? - - : - - } - - - Profile - - -
-
-
- ); -} \ No newline at end of file +
+ { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + router.push(RouterProfile.katalogOLD + `${dataUser?.profile}`, { + scroll: false, + }); + } + }} + > + + {dataUser.profile === undefined || dataUser?.profile === null ? ( + + ) : ( + + )} + + + Profile + + +
+
+
+ ); +} diff --git a/src/app_modules/home/view_home_new.tsx b/src/app_modules/home/view_home_new.tsx index 8fbe5441..de88ed02 100644 --- a/src/app_modules/home/view_home_new.tsx +++ b/src/app_modules/home/view_home_new.tsx @@ -16,112 +16,120 @@ import notifikasi_countUserNotifikasi from "../notifikasi/fun/count/fun_count_by import BodyHome from "./component/body_home"; import FooterHome from "./component/footer_home"; import { apiGetDataHome } from "./fun/get/api_home"; +import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; -export default function HomeViewNew({ countNotifikasi }: { countNotifikasi: number; }) { - const [countNtf, setCountNtf] = useState(countNotifikasi); - const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); - const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); - const [dataUser, setDataUser] = useState({}) - const router = useRouter(); +export default function HomeViewNew({ + countNotifikasi, +}: { + countNotifikasi: number; +}) { + const [countNtf, setCountNtf] = useState(countNotifikasi); + const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); + const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); + const [dataUser, setDataUser] = useState({}); + const router = useRouter(); + useShallowEffect(() => { + onLoadNotifikasi({ + onLoad(val) { + setCountNtf(val); + }, + }); - useShallowEffect(() => { - onLoadNotifikasi({ - onLoad(val) { - setCountNtf(val); - }, - }); + setCountNtf(countLoadNtf as any); + }, [countLoadNtf, setCountNtf]); - setCountNtf(countLoadNtf as any); - }, [countLoadNtf, setCountNtf]); + useShallowEffect(() => { + setCountNtf(countNtf + newUserNtf); + setNewUserNtf(0); + }, [newUserNtf, setCountNtf]); - useShallowEffect(() => { - setCountNtf(countNtf + newUserNtf); - setNewUserNtf(0); - }, [newUserNtf, setCountNtf]); + async function onLoadNotifikasi({ onLoad }: { onLoad: (val: any) => void }) { + const loadNotif = await notifikasi_countUserNotifikasi(); + onLoad(loadNotif); + } - async function onLoadNotifikasi({ onLoad }: { onLoad: (val: any) => void }) { - const loadNotif = await notifikasi_countUserNotifikasi(); - onLoad(loadNotif); - } + useShallowEffect(() => { + cekUserLogin(); + }, []); - useShallowEffect(() => { - cekUserLogin(); - }, []); - - - async function cekUserLogin() { - try { - const response = await apiGetDataHome("?cat=cek_profile") - if (response.success) { - setDataUser(response.data); - } - } catch (error) { - console.error(error); + async function cekUserLogin() { + try { + const response = await apiGetDataHome("?cat=cek_profile"); + if (response.success) { + setDataUser(response.data); } - } + } catch (error) { + console.error(error); + } + } - return ( - <> - { - if (dataUser.profile === undefined || dataUser?.profile === null) { - ComponentGlobal_NotifikasiPeringatan("Lengkapi Profile"); - } else { - router.push(RouterUserSearch.main, { scroll: false }); - } - }} - > - - + return ( + <> + { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + router.push(RouterUserSearch.main, { scroll: false }); } - customButtonRight={ - { - if (dataUser.profile === undefined || dataUser?.profile === null) { - ComponentGlobal_NotifikasiPeringatan("Lengkapi Profile"); - } else { - router.push(RouterNotifikasi.categoryApp({ name: "semua" }), { - scroll: false, - }); - } - }} - > - { - countNotifikasi > 0 - ? - - {countNotifikasi > 99 ? "99+" : countNotifikasi} - - } - > - - - : - - } - - - } - /> + }} + > + + } - - footer={} - > - - - - ); + customButtonRight={ + { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + router.push( + RouterNotifikasi.categoryApp({ name: "semua" }), + { + scroll: false, + } + ); + } + }} + > + {countNotifikasi > 0 ? ( + + {countNotifikasi > 99 ? "99+" : countNotifikasi} + + } + > + + + ) : ( + + )} + + } + /> + } + footer={} + > + + + + ); } diff --git a/src/app_modules/job/main/beranda/ui_beranda.tsx b/src/app_modules/job/main/beranda/ui_beranda.tsx deleted file mode 100644 index 0783e9f7..00000000 --- a/src/app_modules/job/main/beranda/ui_beranda.tsx +++ /dev/null @@ -1,124 +0,0 @@ -"use client"; - -import { gs_jobTiggerBeranda } from "@/app/lib/global_state"; -import { RouterJob } from "@/app/lib/router_hipmi/router_job"; -import ComponentGlobal_CreateButton from "@/app_modules/_global/component/button_create"; -import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; -import { Box, Center, Loader, Stack, TextInput } from "@mantine/core"; -import { useShallowEffect } from "@mantine/hooks"; -import { IconSearch } from "@tabler/icons-react"; -import { useAtom } from "jotai"; -import _ from "lodash"; -import { ScrollOnly } from "next-scroll-loader"; -import { useState } from "react"; -import { - Job_ComponentButtonUpdateBeranda, - Job_ComponentSkeletonBeranda, -} from "../../component"; -import ComponentJob_BerandaCardView from "../../component/beranda/card_view"; -import { job_getAllListPublish } from "../../fun/get/get_all_publish"; -import { MODEL_JOB } from "../../model/interface"; -import { API_RouteJob } from "@/app/lib/api_user_router/route_api_job"; - -export function Job_UiBeranda() { - const [data, setData] = useState(null); - const [activePage, setActivePage] = useState(1); - const [isSearch, setIsSearch] = useState(""); - - // Notifikasi - const [isShowUpdate, setIsShowUpdate] = useState(false); - const [isTriggerJob, setIsTriggerJob] = useAtom(gs_jobTiggerBeranda); - - useShallowEffect(() => { - if (isTriggerJob == true) { - setIsShowUpdate(true); - } - }, [isTriggerJob]); - - useShallowEffect(() => { - setIsTriggerJob(false); - setIsShowUpdate(false); - onLoadNewData(); - }, []); - - async function onSearch(text: string) { - setIsSearch(text); - const loadData = await job_getAllListPublish({ - page: activePage, - search: text, - }); - setData(loadData as any); - setActivePage(1); - } - - async function onLoadNewData() { - const loadData = await fetch(API_RouteJob.get_all({ page: activePage })); - const res = await loadData.json(); - - setData(res.data); - } - - return ( - <> - - {isShowUpdate && ( - { - setIsShowUpdate(val); - setIsTriggerJob(val); - }} - onSetData={(val: any[]) => { - setData(val); - }} - /> - )} - - - - } - placeholder="Pekerjaan apa yang anda cari ?" - onChange={(val) => { - onSearch(val.currentTarget.value); - }} - /> - - {_.isNull(data) ? ( - - ) : _.isEmpty(data) ? ( - - ) : ( - // --- Main component --- // - ( -
- -
- )} - data={data} - setData={setData as any} - moreData={async () => { - const loadData = await job_getAllListPublish({ - page: activePage + 1, - search: isSearch, - }); - - setActivePage((val) => val + 1); - - return loadData; - }} - > - {(item) => } -
- )} -
- - ); -} diff --git a/src/app_modules/job/main/beranda/view_beranda.tsx b/src/app_modules/job/main/beranda/view_beranda.tsx index d6004905..f9c905b5 100644 --- a/src/app_modules/job/main/beranda/view_beranda.tsx +++ b/src/app_modules/job/main/beranda/view_beranda.tsx @@ -1,10 +1,124 @@ +"use client"; + +import { API_RouteJob } from "@/app/lib/api_user_router/route_api_job"; +import { gs_jobTiggerBeranda } from "@/app/lib/global_state"; +import { RouterJob } from "@/app/lib/router_hipmi/router_job"; +import ComponentGlobal_CreateButton from "@/app_modules/_global/component/button_create"; +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import { Center, Loader, Stack, TextInput } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconSearch } from "@tabler/icons-react"; +import { useAtom } from "jotai"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useState } from "react"; +import { + Job_ComponentButtonUpdateBeranda, + Job_ComponentSkeletonBeranda, +} from "../../component"; +import ComponentJob_BerandaCardView from "../../component/beranda/card_view"; import { MODEL_JOB } from "../../model/interface"; -import { Job_UiBeranda } from "./ui_beranda"; export default function Job_ViewBeranda() { + const [data, setData] = useState(null); + const [activePage, setActivePage] = useState(1); + const [isSearch, setIsSearch] = useState(""); + + // Notifikasi + const [isShowUpdate, setIsShowUpdate] = useState(false); + const [isTriggerJob, setIsTriggerJob] = useAtom(gs_jobTiggerBeranda); + + useShallowEffect(() => { + if (isTriggerJob == true) { + setIsShowUpdate(true); + } + }, [isTriggerJob]); + + useShallowEffect(() => { + setIsTriggerJob(false); + setIsShowUpdate(false); + onLoadNewData(); + }, []); + + async function onSearch(text: string) { + setIsSearch(text); + const loadData = await fetch( + API_RouteJob.get_all({ page: activePage, search: text }) + ); + const res = await loadData.json(); + + setData(res.data as any); + setActivePage(1); + } + + async function onLoadNewData() { + const loadData = await fetch(API_RouteJob.get_all({ page: activePage })); + const res = await loadData.json(); + // console.log(res.data); + setData(res.data); + } + return ( <> - + + {isShowUpdate && ( + { + setIsShowUpdate(val); + setIsTriggerJob(val); + }} + onSetData={(val: any[]) => { + setData(val); + }} + /> + )} + + + + } + placeholder="Pekerjaan apa yang anda cari ?" + onChange={(val) => { + onSearch(val.currentTarget.value); + }} + /> + + {_.isNull(data) ? ( + + ) : _.isEmpty(data) ? ( + + ) : ( + // --- Main component --- // + ( +
+ +
+ )} + data={data} + setData={setData as any} + moreData={async () => { + const loadData = await fetch( + API_RouteJob.get_all({ page: activePage, search: isSearch }) + ); + + const res = await loadData.json(); + setActivePage((val) => val + 1); + + return res.data; + }} + > + {(item) => } +
+ )} +
); } diff --git a/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx b/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx index 41c037b9..6af70436 100644 --- a/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx +++ b/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx @@ -12,25 +12,22 @@ import _ from "lodash"; import { useRouter } from "next/navigation"; import { useState } from "react"; import funCreatePortofolio from "../../fun/fun_create_portofolio"; -import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; -import { DIRECTORY_ID } from "@/app/lib"; export function Portofolio_ComponentButtonSelanjutnya({ profileId, dataPortofolio, - file, dataMedsos, + imageId, }: { profileId: string; dataPortofolio: MODEL_PORTOFOLIO_OLD; - file: File; dataMedsos: any; + imageId: string }) { const router = useRouter(); const [loading, setLoading] = useState(false); async function onSubmit() { - setLoading(true); const porto = { namaBisnis: dataPortofolio.namaBisnis, masterBidangBisnisId: dataPortofolio.masterBidangBisnisId, @@ -39,34 +36,33 @@ export function Portofolio_ComponentButtonSelanjutnya({ deskripsi: dataPortofolio.deskripsi, }; - if (_.values(porto).includes("")) - return ComponentGlobal_NotifikasiPeringatan("Lengkapi Data"); - - const uploadFileToStorage = await funGlobal_UploadToStorage({ - file: file, - dirId: DIRECTORY_ID.portofolio_logo, - }); - - if (!uploadFileToStorage.success) - return ComponentGlobal_NotifikasiPeringatan("Gagal upload gambar"); - - const res = await funCreatePortofolio({ - profileId: profileId, - data: dataPortofolio as any, - medsos: dataMedsos, - fileId: uploadFileToStorage.data.id, - }); - if (res.status === 201) { - ComponentGlobal_NotifikasiBerhasil("Berhasil disimpan"); - router.replace(RouterMap.create + res.id, { scroll: false }); - } else { - ComponentGlobal_NotifikasiGagal("Gagal disimpan"); + try { + setLoading(true); + if (_.values(porto).includes("")) { + return ComponentGlobal_NotifikasiPeringatan("Lengkapi Data"); + } + const res = await funCreatePortofolio({ + profileId: profileId, + data: dataPortofolio as any, + medsos: dataMedsos, + fileId: imageId, + }); + if (res.status === 201) { + ComponentGlobal_NotifikasiBerhasil("Berhasil disimpan"); + router.replace(RouterMap.create + res.id, { scroll: false }); + } else { + ComponentGlobal_NotifikasiGagal("Gagal disimpan"); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false); } } return ( <> + )} + + + + + {/* Upload File */} + + + + {!filePdf ? ( + + Upload File Prospektus + + ) : ( + + + + + {filePdf.name} + + + +
+ +
+
+
+ )} +
+ + + { + try { + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + setFPdf(buffer); + setFilePdf(files); + } catch (error) { + console.log(error); + } + }} + > + {(props) => ( + + )} + + +
+ + + { + setValue({ + ...value, + title: val.target.value, + }); + }} + /> + + Rp.} + min={0} + withAsterisk + label="Dana Dibutuhkan" + placeholder="0" + value={target} + onChange={(val) => { + // console.log(typeof val) + const match = val.currentTarget.value + .replace(/\./g, "") + .match(/^[0-9]+$/); + + if (val.currentTarget.value === "") return setTarget(0 + ""); + if (!match?.[0]) return null; + + const nilai = val.currentTarget.value.replace(/\./g, ""); + const targetNilai = Intl.NumberFormat("id-ID").format(+nilai); + + onTotalLembar({ + target: +nilai, + harga: +value.hargaLembar, + }); + + setTarget(targetNilai); + setValue({ + ...value, + targetDana: +nilai, + }); + }} + /> + + Rp.} + min={0} + withAsterisk + label="Harga Per Lembar" + placeholder="0" + value={harga} + onChange={(val) => { + try { + // console.log(typeof +val.currentTarget.value); + + const match = val.currentTarget.value + .replace(/\./g, "") + .match(/^[0-9]+$/); + + if (val.currentTarget.value === "") return setHarga(0 + ""); + + if (!match?.[0]) return null; + + const nilai = val.currentTarget.value.replace(/\./g, ""); + const targetNilai = Intl.NumberFormat("id-ID").format(+nilai); + + onTotalLembar({ + harga: +nilai, + target: +value.targetDana, + }); + + setHarga(targetNilai); + setValue({ + ...value, + hargaLembar: +nilai, + }); + } catch (error) { + console.log(error); + } + }} + /> + + + + + % + + } + withAsterisk + type="number" + label={"Rasio Keuntungan / ROI %"} + placeholder="Masukan rasio keuntungan" + onChange={(val) => { + setValue({ + ...value, + roi: _.toNumber(val.target.value), + }); + }} + /> + + ({ value: e.id, label: e.name }))} + onChange={(val) => { + setValue({ + ...(value as any), + periodeDevidenId: val, + }); + }} + /> + + ({ + value: e.id, + label: e.name + " " + "hari", + }))} + value={data?.masterPencarianInvestorId} + onChange={(val) => { + setData({ + ...(data as any), + masterPencarianInvestorId: val, + }); + }} + /> + + ({ + value: e.id, + label: e.name + " " + "bulan", + }))} + value={data?.masterPembagianDevidenId} + onChange={(val) => { + setData({ + ...(data as any), + masterPembagianDevidenId: val, + }); + }} + /> + + + + } + + + ); +} From 4dd98c6183c3ad8534f363ab6f80a05757f9e678 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 16 Dec 2024 13:58:27 +0800 Subject: [PATCH 07/23] chore(release): 1.2.30 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e67762c4..56cbce3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [1.2.30](https://github.com/bipproduction/hipmi/compare/v1.2.29...v1.2.30) (2024-12-16) + ## [1.2.29](https://github.com/bipproduction/hipmi/compare/v1.2.28...v1.2.29) (2024-12-13) ## [1.2.28](https://github.com/bipproduction/hipmi/compare/v1.2.27...v1.2.28) (2024-12-12) diff --git a/package.json b/package.json index 22e9d9ef..5091e4d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.29", + "version": "1.2.30", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 63d6d48f639cf6054ce65afea40735839c2cd879 Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 16 Dec 2024 15:16:55 +0800 Subject: [PATCH 08/23] upd: investasi Deskripsi: - api pada bursa investasi No Issues --- src/app/api/new/investasi/route.ts | 112 ++++++++++++++++++ src/app/dev/investasi/main/page.tsx | 8 +- .../_component/main/com_card_beranda_new.tsx | 108 +++++++++++++++++ .../investasi/_lib/api_interface.ts | 5 + .../investasi/_lib/type_investasi.ts | 9 ++ src/app_modules/investasi/_ui/index.ts | 2 + .../investasi/_view/main/skeleton_beranda.tsx | 31 +++++ .../investasi/_view/main/view_beranda_new.tsx | 97 +++++++++++++++ 8 files changed, 368 insertions(+), 4 deletions(-) create mode 100644 src/app/api/new/investasi/route.ts create mode 100644 src/app_modules/investasi/_component/main/com_card_beranda_new.tsx create mode 100644 src/app_modules/investasi/_view/main/skeleton_beranda.tsx create mode 100644 src/app_modules/investasi/_view/main/view_beranda_new.tsx diff --git a/src/app/api/new/investasi/route.ts b/src/app/api/new/investasi/route.ts new file mode 100644 index 00000000..a8939023 --- /dev/null +++ b/src/app/api/new/investasi/route.ts @@ -0,0 +1,112 @@ +import { prisma } from "@/app/lib"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import _ from "lodash"; +import moment from "moment"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + +// GET ALL DATA INVESTASI +export async function GET(request: Request) { + try { + let dataFix + const { searchParams } = new URL(request.url) + const kategori = searchParams.get("cat") + const status = searchParams.get("status") + const page = searchParams.get("page") + const dataSkip = Number(page) * 5 - 5; + + if (kategori == "bursa") { + const data = await prisma.investasi.findMany({ + where: { + masterStatusInvestasiId: "1", + masterProgresInvestasiId: "1", + }, + select: { + id: true, + MasterPencarianInvestor: true, + countDown: true, + progress: true, + }, + }); + + for (let a of data) { + if ( + (a.MasterPencarianInvestor?.name as any) - + moment(new Date()).diff(new Date(a.countDown as any), "days") <= + 0 + ) { + await prisma.investasi.update({ + where: { + id: a.id, + }, + data: { + masterProgresInvestasiId: "3", + }, + }); + } + + if (a.progress === "100") { + await prisma.investasi.update({ + where: { + id: a.id, + }, + data: { + masterProgresInvestasiId: "2", + }, + }); + } + } + + // cek data yang lewat + // klo ada, update status + const dataAwal = await prisma.investasi.findMany({ + take: 5, + skip: dataSkip, + orderBy: [ + { + masterProgresInvestasiId: "asc", + }, + { + countDown: "desc", + }, + ], + where: { + masterStatusInvestasiId: "1", + }, + select: { + id: true, + imageId: true, + title: true, + progress: true, + countDown: true, + MasterPencarianInvestor: { + select: { + name: true + } + } + } + }); + + dataFix = dataAwal.map((v: any) => ({ + ..._.omit(v, ["MasterPencarianInvestor"]), + pencarianInvestor: v.MasterPencarianInvestor.name + })) + + } else if (kategori == "portofolio") { + const userLoginId = await funGetUserIdByToken() + if (userLoginId == null) { + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, user id tidak ada" }, { status: 500 }); + } + } + + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti ", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/dev/investasi/main/page.tsx b/src/app/dev/investasi/main/page.tsx index bc3fe278..7951c323 100644 --- a/src/app/dev/investasi/main/page.tsx +++ b/src/app/dev/investasi/main/page.tsx @@ -1,12 +1,12 @@ -import { Investasi_UiBeranda } from "@/app_modules/investasi/_ui"; -import { investasi_funGetAllPublish } from "@/app_modules/investasi/fun/get_all_investasi"; +import { Investasi_ViewBerandaNew } from "@/app_modules/investasi/_ui"; export default async function Page() { - const allData = await investasi_funGetAllPublish({ page: 1 }); + // const allData = await investasi_funGetAllPublish({ page: 1 }); return ( <> - + {/* */} + ); } diff --git a/src/app_modules/investasi/_component/main/com_card_beranda_new.tsx b/src/app_modules/investasi/_component/main/com_card_beranda_new.tsx new file mode 100644 index 00000000..02f66801 --- /dev/null +++ b/src/app_modules/investasi/_component/main/com_card_beranda_new.tsx @@ -0,0 +1,108 @@ +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { Warna } from "@/app/lib/warna"; +import { MainColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_CardStyles, ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; +import { Box, Grid, Group, Progress, Stack, Text } from "@mantine/core"; +import { IconCircleCheck, IconXboxX } from "@tabler/icons-react"; +import moment from "moment"; +import { useRouter } from "next/navigation"; +import { IDataInvestasiBursa } from "../../_lib/type_investasi"; + +export function Investasi_ComponentCardBerandaNew({ data }: { data: IDataInvestasiBursa; }) { + const router = useRouter() + + return ( + <> + { + router.push(NEW_RouterInvestasi.detail_main({ id: data.id }), { + scroll: false, + }); + }} + > + + + + + + + + + {data?.title} + + + + + {data?.progress === "100" ? ( + + + + Selesai + + + ) : ( + + {+data?.pencarianInvestor - + moment(new Date()).diff( + new Date(data?.countDown), + "days" + ) <= + 0 ? ( + + + + Waktu Habis + + + ) : ( + + Sisa waktu: + + {Number(data?.pencarianInvestor) - + moment(new Date()).diff( + new Date(data?.countDown), + "days" + )} + + Hari + + )} + + )} + + + + + + + + ); +} diff --git a/src/app_modules/investasi/_lib/api_interface.ts b/src/app_modules/investasi/_lib/api_interface.ts index 0dbb9c42..eb9aafda 100644 --- a/src/app_modules/investasi/_lib/api_interface.ts +++ b/src/app_modules/investasi/_lib/api_interface.ts @@ -6,4 +6,9 @@ export const apiGetMasterInvestasi = async (path?: string) => { export const apiGetOneInvestasiById = async (path: string) => { const response = await fetch(`/api/new/investasi/${path}`) return await response.json().catch(() => null) +} + +export const apiGetAllInvestasi = async (path?: string) => { + const response = await fetch(`/api/new/investasi${(path) ? path : ''}`) + return await response.json().catch(() => null) } \ No newline at end of file diff --git a/src/app_modules/investasi/_lib/type_investasi.ts b/src/app_modules/investasi/_lib/type_investasi.ts index 8972629d..b794fb1b 100644 --- a/src/app_modules/investasi/_lib/type_investasi.ts +++ b/src/app_modules/investasi/_lib/type_investasi.ts @@ -12,4 +12,13 @@ export interface IDataInvestasi { masterPencarianInvestorId: string masterPeriodeDevidenId: string masterPembagianDevidenId: string +} + +export interface IDataInvestasiBursa { + id: string + title: string + imageId: string + progress: string + countDown: string + pencarianInvestor: string } \ No newline at end of file diff --git a/src/app_modules/investasi/_ui/index.ts b/src/app_modules/investasi/_ui/index.ts index 39103b48..6d51522c 100644 --- a/src/app_modules/investasi/_ui/index.ts +++ b/src/app_modules/investasi/_ui/index.ts @@ -25,6 +25,7 @@ import { Investasi_UiRekapBerita } from "./detail/ui_rekap_berita"; import { Investasi_UiCreateBerita } from "./create/ui_create_berita"; import { Investasi_UiDetailBerita } from "./detail/ui_berita"; import { Investasi_UiEditInvestasiNew } from "./edit/ui_edit_investasi_new"; +import { Investasi_ViewBerandaNew } from "../_view/main/view_beranda_new"; export { Investasi_UiProsesPembelian }; export { Investasi_UiMetodePembayaran }; @@ -53,3 +54,4 @@ export { Investasi_UiRekapBerita }; export { Investasi_UiCreateBerita }; export { Investasi_UiDetailBerita }; export { Investasi_UiEditInvestasiNew } +export { Investasi_ViewBerandaNew } diff --git a/src/app_modules/investasi/_view/main/skeleton_beranda.tsx b/src/app_modules/investasi/_view/main/skeleton_beranda.tsx new file mode 100644 index 00000000..cc16d4bb --- /dev/null +++ b/src/app_modules/investasi/_view/main/skeleton_beranda.tsx @@ -0,0 +1,31 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Box, Grid, Skeleton } from "@mantine/core"; + +export default function SkeletonInvestasiBursa() { + return ( + <> + {[...Array(4)].map((_, index) => ( + + + + + + + + {[...Array(3)].map((_, i) => ( + + + + + + + + ))} + + + + + ))} + + ); +} \ No newline at end of file diff --git a/src/app_modules/investasi/_view/main/view_beranda_new.tsx b/src/app_modules/investasi/_view/main/view_beranda_new.tsx new file mode 100644 index 00000000..4ee233a5 --- /dev/null +++ b/src/app_modules/investasi/_view/main/view_beranda_new.tsx @@ -0,0 +1,97 @@ +'use client' +import { RouterInvestasi_OLD } from "@/app/lib/router_hipmi/router_investasi"; +import ComponentGlobal_CreateButton from "@/app_modules/_global/component/button_create"; +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; +import mqtt_client from "@/util/mqtt_client"; +import { Box, Center } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useState } from "react"; +import { Investasi_ComponentButtonUpdateBeranda } from "../../_component"; +import { Investasi_ComponentCardBerandaNew } from "../../_component/main/com_card_beranda_new"; +import { apiGetAllInvestasi } from "../../_lib/api_interface"; +import { IDataInvestasiBursa } from "../../_lib/type_investasi"; +import SkeletonInvestasiBursa from "./skeleton_beranda"; + +export function Investasi_ViewBerandaNew() { + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [isNewPost, setIsNewPost] = useState(false); + const [loading, setLoading] = useState(true) + + useShallowEffect(() => { + mqtt_client.subscribe("Beranda_Investasi"); + + mqtt_client.on("message", (topic, message) => { + const newPost = JSON.parse(message.toString()); + setIsNewPost(newPost); + }); + }, []); + + + async function getDataInvestasi() { + try { + setLoading(true) + const response = await apiGetAllInvestasi(`?cat=bursa&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataInvestasi() + }, []); + + return ( + <> + {isNewPost && ( + { + setData(val.data); + setIsNewPost(val.isNewPost); + }} + /> + )} + + + + { + loading ? + : + _.isEmpty(data) ? ( + + ) : ( + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllInvestasi(`?cat=bursa&page=${pageNew}`); + setActivePage((val) => val + 1); + + return loadData; + }} + > + {(item) => } +
+ ) + + } +
+ + ); +} From 0bdc25b1e62d6d95307002a0e2af1f58948c14e9 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 16 Dec 2024 15:41:02 +0800 Subject: [PATCH 09/23] Feature logs Deskripsi: - Fitur baru log untuk melihat error pada server upload - Baru di terapkan di create profile --- .gitignore | 3 + bun.lockb | Bin 708596 -> 716668 bytes src/app/api/image/delete/route.ts | 53 ++++ src/app/api/image/upload/route.ts | 58 ++++ src/app/api/upload/route.ts | 51 ---- src/app/zCoba/page.tsx | 2 +- .../fun/delete/fun_delete_file_by_id.tsx | 60 ++-- .../_global/fun/get/fun_get_directory_name.ts | 11 + src/app_modules/_global/fun/get/index.ts | 3 +- .../fun/upload/fun_upload_to_storage.ts | 38 +-- .../katalog/profile/create/view.tsx | 264 ++---------------- .../profile/create/view_upload_background.tsx | 170 +++++++++++ .../profile/create/view_upload_foto.tsx | 216 ++++++++++++++ .../katalog/profile/fun/fun_create_profile.ts | 5 +- src/app_modules/zCoba/ui_coba_upload_file.tsx | 2 +- src/middleware.ts | 3 +- src/util/backendLogger.ts | 37 +++ src/util/clientLogger.ts | 87 ++++++ src/util/frontend-logger.ts | 101 +++++++ 19 files changed, 815 insertions(+), 349 deletions(-) create mode 100644 src/app/api/image/delete/route.ts create mode 100644 src/app/api/image/upload/route.ts delete mode 100644 src/app/api/upload/route.ts create mode 100644 src/app_modules/_global/fun/get/fun_get_directory_name.ts create mode 100644 src/app_modules/katalog/profile/create/view_upload_background.tsx create mode 100644 src/app_modules/katalog/profile/create/view_upload_foto.tsx create mode 100644 src/util/backendLogger.ts create mode 100644 src/util/clientLogger.ts create mode 100644 src/util/frontend-logger.ts diff --git a/.gitignore b/.gitignore index 61b4bc33..924563ab 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,9 @@ yarn-error.log* # vercel .vercel +# logs +logs + # typescript *.tsbuildinfo next-env.d.ts diff --git a/bun.lockb b/bun.lockb index 88eaa6ad3e0e2d12a92a93c78751e54fe7ad9b20..f486e079beb2452bdedf0d7c49d7fd7dbd4d4ed8 100755 GIT binary patch delta 148191 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�Qsx6{NrqrqOd((4|PMv#nio?e36ld3Sk51`u`Ty?GsejJ@hXq*7 zY~Sr^S44h2dfItM*Z(r+mTg}>@zp0E_ue_BRbE!ed(pSV{IT|dvQ>Y!IQB@NmW>ZD z7+lkFRd-Lx?XNZMzj}I5$%yW2D_%G~h8cN$&dgB%7vkT}@4VuSkjH$hPn=)+@_i?! zFR8ib!O;0%UUzWOx05FQmU+=d^>;4+>iw+8*0*l=+o!Ky5VY^2oqPOGJ)AtCKIw<_ z$J4%>yKk1QnByCqR(ohq$9dnTudey@gW^2{{)jIsJKgheqeH#hUv*PtzqK#i_r;rQ zKG@l>O_LpypF0)VC1m#AVc~<;f3k1Nf;`Xj-GU$F`lLnxAPnO+>tx395?Is zfJY|HzwM1}FJ3%0wD<6ClaAh#d|<_|H^p2@u8TQ7e0CV z)wi#mRXb(G!=DbE{=5y#^bNM{T^kMcZQ^4y8#lVc{O9@xSWve{Lp@DxWTx+(RxHR^ z;5+o+KbFz3c)y$P@cFv${vYc`J%3xwQ>}+TQ9bYd{iBz}?YTMX1;6&)y9ExoW%>s- zVO5-Ky|s&rPG{NZdA`9nPkQ`VUXL|(0~QzDmVQBX z;5WI?btpP~&E8(OuSxmqKt$kuhtAvmP1L>(BbsNF%^No(sb%uRL2cWwA5rp0Mb z%l-!*J^D}mk56`6T>f?k|2>6Osav{SvT?RGRu?@w<%?}sG}*E$JofwNK6`NU zbDy2+F>%p6?|A8og}+~O#nY?0r1t2Y{{mlISeE?$o&#xTz9Z?(?y4_!__y;5 z8-FNc-?6O&U*6xLYyG?#kFQ*M`)3~xIM%z{_>isJPc6T#%dk$DwD=*p^>=G#HCsHV z%e7zq-TzI$^iM*^+<)_gn|yMfX=W1^uzKG{?3;VxrmZ2D4m$bhGxud23;5;{pVA%6 zhcCN$?&Mqdy%HV$$d5n0-gfGdmxn(&I=sc^KYn}nq4gJk;4Qppa_^Xo$Tnxbkm$_n z>CZL(+xfL_eY8o=`DM>vpV6!BkY_($`}yAh^>CG#9B^>cj%XxI+jn;M{yX&w={e}+HA20m3^PBzAvn}qP zT-JV1`szNVslA#H{Ot2fR&H&v?vv*SUNFDawkxh1H!|$Lmw)oxQ_hz&PWbi6ThHv7 zdQ)B3zV-FPTF!sz4IkUfeA8xcy)80x+tW=>Z2B;-b;#s9zU%x!T8lrg+mtzG<>}hr zr|mAga81AwpOPV6S9K|ycH7;v-h5=?i=qD4-TM8P#Th#mCEsw?PNP2*@NegL&&G$^ zZO$9H%zO{Ez5D9&mfL=;iHJy#xOVT14_buIzv-&fZ)2`r|9E2SE>G@XIdSjIzjq(n zKJ@Be3b!B1{bTn9?;l!y#HKG|^?Y90`^TgZklcbo1J&s@>cchus{erf;oJ!|#!SKI&X{2Dj^ z@XF5F+u~pHMm_dSOnKAn8hf>U=7W_-uNwMK#;rRic)EY?|MJeBt$Xfl_S4_p-kaI8 z@06e~Zr=Lt54E!=biVPoy(g^eVpi|X>cJ7?k91s+8@+n?yrcWaY*`)BC2~s(zv`jC zl@uSV|M827iMRi=t>v#ZN0KKeto&l#zOv3oPfmIH&&BocM0G0JIPt92YxD2AQT>uy zKasMe{{^2sK0RvYPl@Zod+xm~@!6Qq*8cwabA8t2&UtQV?^{PDZN8=TiVmS0nl_nS zR648wj=^CaVsGm(we6pAAFK(p!>pdA?C~AyX=^2WI7Q?Ko!?ubAH*Rxc@R-e~Bs4ri~-w(p|metG`(7b1Qy_z)gb-BjayLdpoMzz^5O3cGanW_S%XitV`)^&aR<$ zayE;OU#dmVVO=`fx;cRVvp_&5(8&rK&cA}7xj<)2m`jVKY@k%o)dCw%dCviR=K0OGQMD9g

IL2V1|o8RI7`m~Dh1VoJ1u+}kohu@y$l#>Rf5PoAo_V=lw~~+ z)ClSXqb+JVkh2EJTMpc7wSt(nK->#Jg5|yd)C;^TfH4-k0?5w?3I&Pgc@c#~JfKEUCzx(g zYk-^rAa4zjX0?Kt%|P5*Al-7;0`&rKJ}}E-^MU*=K%pSRJnMk?*MY=!z#J!H-MD&z(Ok#1Q!9JuL6rL`Bk7yP$9^&kPSd;F_5tVSZd{h zu&qGEYe2T8zXntass(1@8-dI>f$WXIGOH3qZUdq>0n06G6Hp_l6RfbP0wCutAg=&e zX|;lw?LgdSAlGs?1N8#$7GSl-ZUOS&1_}jv=6M~6-vK1P4y?5Tf&V)|P$96+5(jMk02{1C5WEuzEdn-LauHA_s1OubNHLK5E|5_SY_W1d*n2?4R-n+* zxBA}cdBYB>6j}J2lwzB!vel|o-n72kDBCPcJEg>O zRd(7*m3J-nZOVJLPNmd5J1FnlNR89sz!D&750Fv< z?6nd>@LnKvC$Qg=cLHUC3PHJrybGl612WzPKCyB^*nS}5J)pwU-(%^Y*+G>97G6rJ zw7Dt=txDyP^?jf6xn-$*VaHSsThs@XFKwC1SA3x^<%kU_qf}Y0%GY*Mf}b_2zNQo#=vs1LfN13-m7=#E>+J|MUf$k+$` zY~_M7LBxKb*3$O_sRw~-!3hih2nag_WPb$IS(Ttt5M2)ZVOixs=I212;G{);3`Bkb zO0ie<1ya~Up{^%i4{mAk8PIZ zf92c4F8<8-CSR||?*9ylKSG<*&uG)cV;3Gk{Hu_Z14vVk?UWQtLMxHx9-CZ=Bz^6B zo_$*Bd$VtV$J!r6g1@0n#zESg@3D_1Ws-OuN%R*;D~~Pt0?9mz)Ja-b>5!21=@ z&SJmP);R_g3PQ|t1gIAz9sxR7fgt~TAgBrmwS+1l{s*8`(9r_F2K;~IRd+t+7j@oQ zmsy>xMB|Eoq;cpsH12H4-vCL+feJxa3;7lZ{t3wV7U*u}f-*rwH4tv;)j;acK((N! zg&zgNegU$N0ufdvs1!th2lTP5?|{r&pia=&qH2K1UxBBkt&0%K;;H&`XeRU5>y7;R+Sqq@Hi#L#;Odl5|x{* z^-q+cmQ1mvdS+Yk6SEy=AwL7bCxMKgfm^LyP$r1@1&FouUx3sBo&xet0Hdr{P$P)@4H#{?zp;Mz*h!UpEw+ww zpRH3#FwgIl`)#Dk7%NbDz?%L+Nwfr&2W_j$Ll#)iZ2f&3ahTLI+p$(6C>DgC1jbqN zNg$~aP$8ILA%6nFen7^bz$7adlnEmK0+KELFCf(mR12n9_}@TSV<7u)AjPT#m4fI~ zz*NgR1!Oh>>IBm)>K`D|AISR$c*1H0HG;U)z;w$!4dgU!6p-!Z>Nzbtwh`}tGX@J8 zOgE2*;d+KG(F4q~0zrOrAjlWUumoQq{yd;mFvkKL0sa9%N+V#dl?aLjp?<)8OZEej zS^yP-g%;ukg3kvsv=)o4Tu>&6Xbfc8!Nx%91wj8Mz*3vr1PBWRjtjD_uRl;JSm6(t z9TQ|;2n=fqEVE@zfykD?DZz3Z(hR5(Y-k3ou#o!*sUXh+TLAH`fRq-%S}PIw2Lqw!1M4jLe4toRAy{uA7XV4Efs6}) z4OT7)z66K}1U6cFAW$Z#78F?cg+OW>Ap1gKi&Y82E(M}n0)>{<5~vi^35qQ0A|SIZ zkarQV)oKNimjQ7>z&6Vb0%`=_i-GMHdohsH4k#4tFi$HW=5iph6;NUYf_gzvFz~J= z1OxdYK&hbA0$T&|?SYimzz0?$@b3VGUIOg0Zt^hLH0DG-m5F83b zTng;B^h<#flsVT5Y`chz6_|atjmB(L7m`$MYRJm!+^YYz(K1O zM0Nt=E(boh+{=L)fj0y=Y_TCg&Q(C6;4AaA2Vyz{iS2fcUOJ$`!zORwD54284zJ$1FJ%C>B%*ez1@$fu!z0#+ATvD;ES`4McPVezx?E zK$)OgP;22~Kx#OU9R{4RDnVEeAi5J!XIY(qN7i)8lLRcjM+~y`+?zpT3QJ?ZWOzejg;I zJL2`Vos#%SB=lE(U!+3P)YsaFBgK-8aHP4feJn}328rl_1o+y_9!PLM zq*`*mul4AOlu5FCB7wg4wIuagB)S*U($|*sLc;nZb&?=o8yJC9O7bF*R=)PDB=b5X zt~W2VwdMBKwiy6;`v7e$wvV=rpit1(JdxTq*8_=>KszfC#6$r>eSr{5=nK>fN(CJ( z@ERb0Adqqm5NaiY_(4EuKcJ%}_XGTI04fBXEaY0CSdeio(AmlbNzp(=f1sp!BpXm0osCCBmYvr}VaEDt)Y0CDMjOQTkf0$~AUU zrJuzPWVSUoG26m{%(lOI1_3!kfy6<;04os0+zbTW07O~B4M4r1R4~W_qk;TkKuR?MljpDo0x5!MGXZi1$je(JFQlbc{>nyGceL}Zw4ac z0Piqhl*JAMY6OLX(dM}Y$hiYZyal+|3Is8C0ztO|36^jxP%kJIjIqGmfc$tM)`mrdjTtK;(UZHy(Jx zV&j1tL7`x}c}4;`2|(gVAk7K{G4}&OcLC{^a2HT7C>6}Iz)?W{7$9X7kYOc)_y>T{ zyMZ~Dd^g~q2vi8>TF7XiSdcLqm~Z8Rqz8eBdw_+Oeh(1*5Kt{xY~l9;WrFN`fh?;M zq$UB;_W?^S>pmcCEKn!Nwx|T4QjnJbnAHk09|q#?2bNjx{Xpb6z&i$5Zn0y48bP68 zg?SzTa>fIR4*)BzKoBzl2ucKUEg=!87nBNCTi}C0{zM?u4PM}ay)p+$`cDg}Atfg-CFWTpUd6M(IjI{}D%4De0_wpr{%phi$A*lwOlK+aSk zaT2h@3Is8!K+q#Vi6uM&)C)=l?^<9okUtGbNd`)-1mGq{>&cW4ELo+@cB$;LkSUZ8 zZJNq%D_7ZLogSs^wRDwzc2H%%g{Lsvvgyn=JB8VnTa_U7DIoeW;1kPw3I4-Q zH5I57u>uWhT!Hx~FLqtZ?kpg30pNWa_{(CS25JO_f>Y+n0CE-ri5b9YD-gsi0)l4qNzB(D-#?pA z;(AHjIY^^M_V64ee=)LG;%#J?WFqm;Ak#CECXMVviGLQ-buQAhkv%>aDV7|TG;d^A z%|nuwAPeUq0gddCBzP&(e?D@4BbzrLDU%$R1U9m379gq5A}bakEgRYQlCW%K*g_3L{6o_9AOkWCgv|R%K7l5wM0-bEyvp})n zu%NSb$_A2F01LB$u69rm{36i*IiS1EeGVuS92bOJUjtHC0xJyYX~zU%F9E}HfCyWb z15^r53HsQOWk6;wuwfa{*G>u|R{^7+2m0B%=YbkQz;d9!ja&}otOm9V23XSOqwpCCsXuAR!WMfwV`FX%zLA14g5r|&{On(u$(RK;^*8*Kv0z+)tN}yP9STNK& zy#yrX0}EdQhS@E^w>O%>~K?#|5$0cNLKO3b0}oFv5-r!qx-BRs(UiY&B3R zI3>8#hP(`9z6xx385n6N1(6$o(RsiqTbBpa2m;msqiy6GAm=q;yWn1Hx)zAp2uxTD zB-mC#y`XJAFviB_1NobPy@Et*y$*;k0H&`49O{a9A+TI;{ti zwg3y)0~73^Aoz8l|Es_xoBJwICO9rgw!RyH)Iwmz24IRE6NJ4140{bov1P9Tm4Z`( zsWxOIkXZz5*a%FslY+=%VDu*730t=bs1XDd0Ml(`0g$s5*e*!3rkjD7H-QP8fpps{ zs28-|0?e|pTY&s+z+ORywSFCle+!uYIxxp}3H-MMT?>J^HmwjS791ALw@z;WNpAxS z-vAccK|$~ipnnmt*ya`iWrE{^EbChgq`m{JCTaL47IL z+Lyh}wf1To`!=5^FI$O9p0(aVS!2m6Yi*ZGzJaCFJU9+zLY_`UrL?&{rehu6>l zS$=Q*maTiA*Txmg2b8yMqzacSD(_g+G78r!Dm!hf3KuH7DDT-=6|Pb!7XLA88~P!u z&xOi|fd3~zg@9|7-9WJ*V>iI%iXiDzAYu=|70VtVxB{paaM7|CC=+Dw1-NJtq<#iO z?*q7K*$0Fj0O|x>wCo2e1$p}cE?NYcl|bA_02eJE0g(p*Z#lq4OF2*@C=_tf@-dKe z2uS=G;JQT+^EnXo3BaYxCqTWRRKV5Cr$GJ}K+2~87cheO!$4>Sz%@(-;Qu91A>bP3 zGoVIVZH!r1cd^wVGaX1)j;B5fNK~*%uyid zORfhzes=$tTo2SsN+pf_?82{*{O^#IuMn@F?UcmVAfZQ)CVn>g2;zSXsgN}Fv-VX; zu_U7kY3^qqOOn1vBECif{A}jeNbnCxwd8z1>+uazCdvK=3G}nCC8<9m(cdC1{cOp% zNZ4_tP7>s21FMlrNnSP5%Flk4Wd4N29p#0#w%nuIHa`R2?|?QI`<=Fppit1(JT=-j zzW|9fKszfC#MA;o$AAz^I0n=UN(CJ(@OvQtS0LqkAk<0(@h5=LAApXQ`~%?s8&DzW zWFbES#e$3uC_|0jdSJTKGwzOptvNh_xy~sxJ`zCosaY{sh7r0d;~n zi~0+w6y*H{+-bFfOg|v*Z(yY5{tZNW0q-ebl*OI`Y6OLX(dPLF$Y~5D{sY`=1%j9+ zK+tI*!4ggb^@38tnCw75wuZl7Kz52BS1{Qn48}KQbf|~nhb-9x_%{P81Y<447bq5F z_yXgsT#(cph-d^%u=GYi@OePBV3LLV0cC=0KOotv1gQZ)v=^9SSzaKl1yCnQv8cvC zr68{{Fx6@Wndbv>O@L{Z+XRTb0Py+)Pgtx!P$MW5OgB$cASVz=Yzm}Vfgt8WAgCFT zZVAnRdO@jRmIXEk@>>Ea&4COn5yW2vgq{b?vE=gr{~(}3FxNr?fMP*L05IRm1xXhJ z5iNj)mfivgZUs~e7F+oFK$#%>d?3rJ1gXJ5^aa3D%envvYYo&1vMow)Q>7p;5HPD1 zWL^TqT?j0*+zWxoHh{M!u-sx>0yTm{!3y(S1ms)_Bwhrpv;sj)TOcS1$hCwZpk7ca zSZ#q91NoN$DHj8IRw9UR2ZXi))>?8a!2fcfLa@$4f`MW|Mli76$^}UwKtyX`gQd3y zg4+Ytf{hk_2~Z};z62<+DnV)oAi52(#j@G}VOIckfrk5y8_=?a#z5=H&7usW+B~xVnIeX;0G%gB=rFzx&y~8y*m&b2~-Pyw(zTgGC}s$ zK&@2?Qu_kY;lK&Y3J1cj0qO*G7S#i&6y)^){;*m>WHG9{&wMqazH50XCsN$G=ly|z;l ze?1Z!i8S%rwD2=uh98-cLffI2~hMa2M>g1i`@kJSn?V}ZCKKwrxp0z?i6yf*>; zEcPa#Mo=i|Z=Rt*&IllJC@{bZ1TnV*K{o?YmT)srFDMlZvcO?LejJc842ZT8LHr#+ z=qy%iTY;fgE=Y<8B5nhQS^8~2@JOIqaI1yK0%d~iSRmG_ z1gUoc(ZhiemNgs*8wJz};w)+eP$|e80o-Y|g3P;txZ8n|mU}x8IU4ZB0i!H74yX|n z3Pzjf4j|_qAn^|1UMmp9+zSNV2_#s;oj|>yR4~Q@K>p{karI-)oKNq4*_xa0@Ez_ULZ0F@ZJYJVX^lCHG)FHbn_$t zIb(su1R%`{1ThZ-LH7gcmT*5%FDMnvvcNGw{x~3I43J?Zg81=3=mWqUOMU?Gp8!+{ z=2}Q1P%Owu1m;`0AZa2H@gT6!(jNqZCjr%h#TNb$P$tNJ2*|Q3LFywwbP}-CvXX$X zWS~xvZBb)^Nn!9EpjeRc z2(aGD1xeF@h-6@cr6&Wyj|0_$jTSx`C=+B)1`4c7kop7=Jq6feSyOc zDo|ou1@(fq(|~tv>@*-h9oQ=eW7W`nH<^V~H zfrWE`<91LG{0z`P6ZqNYW&&k`yb0rv!i4 zkoiF7QeeY;;G~@tL_Q0QUI6@M>lOetf`Em|e>CYfd8rz2w|7A$mETm~;dprv%mK>He zZ){gBL6V+F7A`>o8rvaB@N%U8Qsn%`Hg73XCOIw%Y;4y&i=@7Qtauh_+1S39gsnh^ zWg|h2?fGn^QgRCMw6Y=3`Q7OWwp^9gc2eaM^W;$4*hrO2tw5!%HC;xz%o0@E*;bXy zE%13th>fLKeJ-=z`#iJlV6B$}`Ky5G%YjhaC5T@QbbSHnXwzN*{9gtR3p!b+6+p3I z;R>L$9TX(x0sUVDy4u_qSy(r#Qt57eS5mIFER}FOrqaWrUZV80Wh%X_RwcrQ|M^ zvemd>fc=2L(y*1O0aZ z3vKQWAov5|xL~pMeFrELtat~=vSWhOGGJHfmX!|~pYh&LB>IHiR ztF841K>j{p`UgOs?GnWA2fCI4Yi(K?;QtYDSg_7I?E;EF@@rv>cKO}J#l}HFQaLU9 ze@KfBHupmy_+#L>V59Zj4U`F1>;?+#m>~5NVAvjDi!Iv&gnbH}5)|5yy+Ea4!(O1s zP6{$BfYJMat+s9-=ao0jv!AlfMykAJ1uEOE=|_~eEkR|6ZB=>40?R2SHdbY)m8iUH ztv{x`XUQt1woB!G3;BfdflX5>vvQSP*6CBqhnB9g+YYMivG5AYUYo13&#Ef?<`+HF z!~2%+()-5y*(0C%`Fr(+yUmKqZ}m3!JlD+Y@9Vj+Xx{<9TYP;lyQJu{Lw-C?@gh$+ zo^Ry`{01~kwdo5#|3<%D;ZroJhc}{$X6Juqc7InhyLWEkn@fvmyv?K50TaeOFn01I zd{HUiT-wso{k(1L`Wip~i@#-{G|K!5aqXVc2AApbIsPqH+*~YjDq;0 ze`@@?`TD+kUs1Oo{M!393Z3FpbZ?M1!xJzuVdA8*5AdfY9_KrbwQ{SqIK2Yz(kEF3 zQLCa&wSJKvukS3r__*N(7~p4DpYXe);iZf}!5VurSenK@)L=ih<@IctUG&olzt4Od z+82HCyWiy=-`Afl`sWWno^AQ%#fC5Ro$)vIf`-{POsJRd`_3&zJ^u0=-0<3)FM7G* zTU=+{KX#%e-{p;KX!e31tN3M}k56+;sPS9+UmIxE+J^VxfL{~OnWet-kKZO=Z_hV) zeMPL@xQ2N)o0ypTXx9f49~=3${lr`2+rGpWHS!*9_RG5s-%LN_`}gcAKkt>T6H3o4 zTif?V`uS@}GhU%K*Sy*^%*(#;vMT#`X=`a!?8(LqAKp!4y{cA4FE#ew=kfmYk=vXN zZ+}3~9cSL0b|1r<+1iGg{qI{|HmJc`KRhLI${1GQsl#Vhj^8}`Y^6U)^EYjdi{C$T zX5Ge&AKUHWDQCW6;g2f2>^$$+-ZI{|W@omNUN#f^umhY~>c3lQQwwi!!|qpf@zdDeQwdj9^Px31T=f`477jK<8z@l(b6;TLBm1we2M4GE_kA07rE_P z@73P_+8@rmS-ejkcH)pje(lsIVEBg~A0F+WF@_qf`JHK2RDYef#&hPN)<$kPDgxS8 zoN2u6fL0Jbb0mKe#pc*u$+9(r<_#~udD*87GvkFvJVpBt_(h*t&6}fX8q~tWzQ`77 z-lSn2n)>-R>{mh5h68Ry!|ETo*?Vok#Kyip!OUi26JMWp+Pae(X5Vm%S^8wrzFWO5 zJOMvO^H$L4yo-H(F4B7KZkX?_m-sgDT~v2!vX6#)7X(~=ISZnFf7PPei@krHdCQuF z`1|-&h*t&I?(lJ`y!lqFb&h(r?)dNrg>=%u03OEK26-j`r9}+oIxLd zXZmYyG0vJga3YF_FkV`oi)caFZME@+ninK+Hp$siFMWz^V8RyvwQgrci74` z)Zgyur6th?V_Iwd)sF~gmpJ3|%;yqkmpap*-&mqGr@PF}??U>QI&0@le+T2)G>4Zv zya>#87UE2Q*Wy!W?VVkW-R`UdrWMp*n7G4PsB6ao>2s?#2i=v<^rs&50`!)pb#y(K zFc3k%x-e(@qYLLd>qOOiekr-YSr=z*u|Q|tshaU+(hflve#X*#E@xn> z!ycesNeG$gte2a(J$An{t*92#flSq2M%UX-djI!cx?^S=vzuOzaAu$sEc>)(t0xR zKm)O+4s~4rs-@B0(#`X6Wax^{iAf4|87emB2J$XACM-557*U-}yb zG^`_=!!^7Hyu}?(54wi^uo2FZTsxgS;+&0j)*rji*~8AR!@hDWIL_GstgUM|zA@WK zJJR*!D2I*?HGQRtEQ);R4yTFE24a=YCOI2~WjK4p*$o(1OATGJvuK7saW>i6V7*5b z4yQQ0k>SsrJ?bom;Q?+TDb9vq*E@U6*-h9G`gJ%@b*8iBZS<>4b#^oT`{~#6eWp1a zM*l(A@Ns8c-1~g#4!b9u=~VlbvnQSDRC~nPbZ0u%e(mfjXFAh9>x`X0tzlD-0B1X# z;TqnK&2g6QEDpQc*-U45U_G79a&{*c;p}NlJ1ak7_~5juzY8Wo!yJQiiK+XKjZK|`d@Mlvz#Sh z%d`e`OPt+L|8lGob*Zy4^uI-P9(mT;1N672oz5fK&JyV_)aIaj4olOX{vcT_p)=R; zA^JmI!yIQxm=>aw%Q9zU>7PQ>J@4#c`nS2`eL1Gtj3aM3Tj6ZH_P_0*PB<&w#1rU$ z+ckX2*+i_wS+285*ayz^9XwjdBV?DemtDJLY`?QSXOpqR4aW9g18NKFRebFlu53si=vsv`FbL~o;J&m<@w$oV#)&k>mA+;_VZ{PEC9E-)96QT;)PfZ z7DX+0wut^?uHDDZbkrQe22wx4^g^B?t+5ztg=?2Ze~6p*GiOV%OVgV0FWmu$OX=6K zuddSBv-BT#XEU9i_1FoEpGgH4ir+nco2m2V)2`uff!(|M-1?p_| zrL*Vh-{L0z%Gq-47ri%hM=`VP}Nu?SIA?_9b>TE5hSI61P=Y+F-`U{!i1nO_j*3rKR(}}6h*(>yCy9NL5 zY(19a><>Ny8{SWCmK64Cox$pzZJ_@#x2K=Pv|g`~8E)cJuH8m#GN!ZIKh8GMKhm{3 z?W_R18EZIQv)^eyO54mox4VWOhg-10n9hFu5y*yyuhXB$F{`s$BTR2?A-RlpI)Qmz zyEo{+5Yq{)v1?bPezy#N*RB}5p*hE&&R|U)ZlyoQz2auh-o%DFYwm0trnf|Ay7Qd9 zMSmAoP+fqt?ewR*1-yorL%YGzt&BA5ym7w?~%#p@ekd_u3f2S^r*vDuHpOGx4hy^YHMd7$lMvOjhna( zJL>FG*KQZ~v$M-wyAQE0&e}QKjlIM&(&o`~xx+p5=Yg^iXM5?-$JDiVwvYbx&N?{T zk8N^xg|m;a3~W9%6w~2ZPUg6FVNKYO+Wa4rT6cQv zLH`rlzI0vOLO!EkXH9k8UAqJHKR~;esQ-RrU8Ua0!EWMk*YF?>^f{eNdN?~oe>m^w zDr!$>pKH9cUe3P2BHf-I;p{Nh%UN${Uus(HWxfA>K)ukfh<2`Z)N5SBBlJ&Wb8VpZ zb5=$FC??i1el4c+!q-I8>KMP?wflztJ7}jvJj&U(^uNYR>u?^3HC+EyGjID;MZ1Z=!@4oC4znl0ZYZW@{GbEr zG4}pa28OwYKhmG->=tLov7;0&-|6f(`a3eQ_U?FRb@X>~HqzPe*fzIQ-{tHN zY>D=Gx=}K{|MetVZzA2@uHi}g1DuU^_9r%-m##hj9%p~i|CBZd-M!BKra#TKyU*Dv zEZtdxvwzes3Dlu+zr)k?NANyrk00Yq$7lpQlJ?{WoOv)E5!#azo%v!qB4iIbYlI~* zZ58$qrq|?$jX7g!KI2?NuaHfxopHRg#@Gl)*0dz`oA z2ka4N&9KXvpZ1Al%vZ;Mb1(!{H^nv7-^4oR>``X{*zfNBOmW8Tai77wB)x@?IXfR4 z%U0Fqo$BlYOj}i3E!9~d{YhzT25q8g4li^K^(r5C){_1wc_n&9PdK}X{`Rb(R{lw6 zLG*v`R(iU#i?JR|tL`ahtuUP{w4(iV0ZR1==ARBstQpV181K`X{wpwzo9Ws;qsw8s z7k+MR%?~vWEsB4uaf~NzI5dLXPQsg>xp}IarM*c@auEq47n4>bnCQl-Zj|cA=(9xk zJ)a{+a>z2G`<@l#Gctnco@X4=qP-(Uml1mV^>*v+)w{Wu z>?8Y0InlXCry8AFbSlxQL#GOz8gweqk*_0NN4Ab+9l1JEbztg1)Pbi1O$XMkB%Iyu zX6i6<%ccCIQ~W@pGx!ZeXKkIOujju9H13e5kE7lsoOC7KNE_bEOG#UD8EHo@Cn2Og z=|HX^q2x-Uo5i|0+=*O8IuqRl?n=6m?&N9`PI{1DB!cvA%>Cd#^hA=r#TG~o3uiD-ylV#m~16)l5ONIvYot5 zc93^S3E4^BCGU|n>EtPrMrM$7qFd9myzF$N>A9EOM-qtc zPd`8s$%Et}l0?RmhsiiHp6H%+Dw#%fulh+cojgU-NIIEGo+i3)J)6uSbIClih%DC6 zE)Uc53LnF-lKbdSAor43(wQ8=tH{^n8}cnVO1>jslK05_WINeGHW1zG-b7-_a5942 zL3F=c_qTOF`z|snjeqVY_mKNY0=b`zArBDUT#E>E6CNh-VOokEN|Gt&nMqB6{fq&v}l?#oC!ayjWht{`hUL9QdOkXOkDvXN{etI2Yr@9O-P=oYwc zc|T2bn|n6V^}4RlbtS^JcAC!&a3&c|?jiS*`$z)0pNt_7kVLYNl#uI)ZfWZl_LZa~ z=|rv~T}gKmKrSGGtpS zHe-o?L*U1-mn`E|#4|jZ=;ps}_TNFKv4P{LUJj%OsEI_sftbuLHibM&?qm2i{SY5Z z<}g7fxq<#cI9}wNr(k(3Ap3+Bc1<{Ql z-T2XM&OD+!mTO5q(Ot=qjr^nAiF3(3GN0(T2`MC%Otb7A>?XCm*k8#B@*DYu=-$5W zoaB(DMECD?yI!~F`|^4xQ>Tz#^oLQeBArQB(v9ereYnn- zx=-JW=-&M0B!sjl9mo|Vlr$zs+2XoaUqgk&@+i4qXZA7V0g_0zvETKe_9DH>k1XUk`GV{sACf&}FWE;5 zNfCLJq>yFg1+s#?NLG@CWHDJn(#g~0aq2_)}6rJ|hQ6B{@j) z7{7+BCHZ6>d4=dQU6?V83KC+*DL<-0j@;c#{4tF)&YF)@o7Lmo| z3P$Lc3|EqlWF?kMR*@;>QIbL?kcnh6(JvPCiv<0SK)(~%N#3;$Wn2&Jp?5FQ?f#EQ zIr)?vAeH1GIYd4uUl9EY;7d|Pbm#vYqPzV^iSFqdGQX-1lpSzMf-=FRkB?`fp{+mD_v_z=*I^zmc@nMkfAx{)6@Qoyf+(d?w2l%kiP4tJzSfZQf6UYyY`Rxrg|Yt4R;iiagFdo*=6^x4%sC$QqKi zmVaI%x#SShEr5CKRv)q=lQ<1FrZypuVJT!dwt&qymqgP)nA}L(GVU_cj$BUozeYao ziEcx6As3RCB#2y0T9IJg%W6%}C8Q0}CAog7q8pw~Ni(9G?Yg2F4wlQyI;xt-ied`Jl^w^P4MdY7KRc!j#<{~0+zc9Qo< zDS4mhj{fVUki0>P$tF@j<}&?@)JW2oTtoViYe|3d6I<$MqTc{~Og%ly- zmORXi#*qm`zXq5@Ucy(Ar${!LN^T)P@glxp{2}rx{kpHEpKJBQwSGp`56}8xSw9== zXW*Vp)0~_~0?E%z_Y3KNf`5J^b>s)~7Fo{BUL;1c$i3K5ax)o5ZXvglF#d2kACcko z_8>h;FA_m|lRhMp^d;Aje&kxxpR8uKQ%P5TmhVRNn}%?5DI4rEayij26*gmA$m`?{ zQbdZ$R`Mp<)`DH=Eqb<-mq}ZWs>Rf2$Wo%;Bs@oq=r;)Z<-k;uO6D`XfXpFtiEb?D z7X^=Un&?kWC4K0RB#+Rata0QqqF)r;MQ&=rE;N*$STdZ9Ao}G%92r7hW#TAmXX*fI zM-oOlk+oPpxs-li;w8H2--P&+8(B%++aE%1BD$x4Gug~ET}U$fy*CYLOdjJjGL`7& zy>8Y&PNtI?B%RD8v&hpVgUlv#NG6#}=8^ei0a-{Ek!MI2SwfbQXNi#NZR-CF&)_-y z2H!v)$OqcsF9;z}42nYuCCp7g^SP430SgZEe$-;X06m+7n z8Mea?*bVzYCkWq@!FFy3J7E`SU-nPjm!QLh;xLS``gE*GOcPacb1{A7gY>{lzkXp* z0p11uV@(n7@uwP8hYz3uG=xUb7@9(JXaOyu9dv+>&b1B0PCG=@*<^+7a^{xQs?kPH$+BIrXJ9V%=^W}8L_{v_}VqwEZQr-a%+ zEgqd7JRopC9Dwo&pC!{va2002O!x?9!5lC!7v{squmBdpVi*PeVIb(xARXvX;3nma zg4BfTKp>(yf9gUx$N)1DdIhH-57(I?8)##HAH0I+(2w?g1p8nB3<7QR*P_`fLM3<) z@S*F5TGSway&Copc{p@hgB4|8rHyC zSO@DN4w@IKqn69>=y~6VIqV>X$q_iw^8gaWW%2mbo#g%i>!pj zpm+YJz*M+ITtpqlY74Y{AD+M;ptJYWa0c|A#u^v{cd5lvSPo;LH{^w2NCtX|Y(0EN z#$pGRnGbq*YX*#lE}-MILvUDi$wWYA$ObteC**?MkO%TYKFALRU=vo@0V*I`Mt)1U z?g0B4ITztFw1uvqcYdltb;tn8AUPyViY;?suAI;j+Cvx2_5?RS?m=p!*JAX_%Vc;+ z0e3(LLpm_hYcFL$7OV%6a07md$LN6X;30HHb|UTsNDtK@6pCw?^%aG@hBI&rZo}7b z4KBc8*asV70ySTW6?B~Om`sE5@5Mh8f4CDi0Dl3EAjn9X%pm#g>fE4rHfDidzla!# zrw{a10MvjFpeE>*i1#2LrqZhr8A*^y5SA5ELL7(-6$yVAc*VggOfMIx(A1<$3yne7 zrEyDv6CUCBIi@}Vr$C)ZF9PIdhFXNH1ACp%?+2Z$@5a<>k{@ty!5zpB3E&JPV-BYO zkvyhCMYx0PB~Y3@@C(-Z9sY#DDAQZ>{|3x88`=<{^YZr40dyp;b2pu{6&2Jn7;=zN zHc$o)P)fC0NccR^^3E_U7h7 zhq?J63;c>XUt*%aLFcbJTOA2=u!xRNtH5yl#V9xgN}_Zzh;A*{IxPqfht{AYP=%}Z z=}6aBufudi0UdcpPh5)YWz;eZp^BhmOiRMg*t7q7AP?2)=>8jOJ9p!>Y;_j#Zv1?L2G@FTe& zL4MgG8!#pKVE`*G4Q>iZ4FN!F_@$(LKQ#zVrBBb^Bv7Qnl>wv3kLLE{#;NmHE0g57 zG`aO1fYM4X6{LeeNDCPtJ!AnNWQL5839^DL7Ezx+wc%Z;2o<0_l!LNR21212gg`DR z427ToU9`?rFE%;zb@2+&d>=uYB;vU(-<0oEFr-L&=8csP1qcN zGic(3HFaFYix$uU+Cn(AfmR@e6yF-cqg`A62q#h(?V%mG2~@b7nH#2zq+Ac^3SGbr z>lV%5-HB8DMNreKSuenOI0s)s#999IgFet3dV$)wFKEkU0Q3j9&CcLI1t;MId=AH9 z5_|}{iH(I}FgTij2>zik0Y<=Z!1xhN8%p`%EV2hu<@Ww z<#**3<|;BVnqP{z;gcn;fY0C<%y1%);vRv+;ASYfLvRq*LL?l3{opDPy#jj)-viTO zH|&BHuoLFMdRVT^cYw^X9k#+2*bJLMCY1IYVFP>$AHzp51zd%v;g=FqK^9b@GeI?- z4+@(F^I$F*kP;EI`J+e)D)HN{5-ZFtKw2x~1x~oyc`~RuB0)+l1f`Lcl;;{)4Xa=! ztbpaPOvaT~i$Jwm3ZK9dSPaS}(sAW?YpDJaU0K)fDzlFGn5?7bQ3YJ(r>Sw3sT-j} zR1*ow?`EjBcjHz#{zzO|j#|8tRoZRfc2Fszj_Kwlc{R1Jtuoa9k)VXC)jn_wio~zJ zAtj__^l-JWOseLK1X)1oT^V(_*!-V4amqI$dfTbDs&^`({BomP!1cS~u2mE+{}(Z& zk>cvmZW@($8dPD@bg+71^hjkC3F@7i^>X|3z=HoVT!J6r2lyTy!UOmg?!z~55AMPp z_!@4*EpzQ2r&pKpe&rzl6}Sr5;5yuZoA4ck!z1uPX7~kO!q4y&eu5|PC%k~a;V<|D zeuv-SS9lK3AWVH-nJ9rGmH0Ji!z?Fg4_*)3dVx)A2wlg4xQ;&nS9|o@vyTt)KyT%F zK`+y3KVN(IdIL`{(kZ?6_O++4_uJCwO-Q}KmKC%+ml*;f6X=DtjG%CZ>Af_$KF9#F zL=Ie~Rk|#Wn;kbBs4(q;X!Teh+1jApzjtv5;#Sl(PBtb^yzQwiD}c-2i$)G000;F5J?%ArK6Op%4^+{Gi@b85y}jTt^h-PZ3B;AU{yx zR}8}Nm%=Rpp->!3LK$cXszpVp2_HZu(AK54FyDuApm@nug({#ePRS{}0))Z4`VvL~ zmBEcvgu>)1k?VI0b=?}o$uCzjO0R3}oz{c8&UGE!+E5FWr>>)v!TxT+^@)&R^g`dh zmW(owF4L52w=h{mR**v4yln!?zcILqMHSBWWdsq;K$`V|9?%`MliLNfyW0^uKznEh zZJ`achHz*FEkSMS{^$0MaGJ6+aos>+y>WX%Pv{E~3LJ)KDolaNFbO8YhcE%g!#EfV zV_-B$aknsayfE^K#2p1AL3x~pFW?j`fs%!QGpS%52rKGFDJhDSmxVFj#$)vyLO z!$#NupMsQB!fmh#wt!@|!gf#|ir))+Kl3)2gG}t2I(nOaNq9kOcn9CV-j1vfu@Px*dN z@yKL9JMJ^wU*RSE4T*^R1NV3M4gQ2w@E1ttg+m+)@qiz^Cd?ln_1G)C<8sb5E zuH)h!Rr^a=0f`7m3GYBMNC7%vNdiehVXm7RzX}h)O#`xoUL4R?g8cf=wTxaW2!-4z zlN~o3WQ8mcp=0MDC;&Q!E(|%K5V+wv@fQTi ztA3UK4cO zUIVH_HFzJ~{6EuP$6*4F!G6#^dLQmC*a_O>u1MH6+<7nubTZHu+JJ7`y`csE=HO_ls_}LR<67*-zZdp^t~I9|#jkMnu_JI04uE9p5#LMguSf|U$FI5lkaO)O zRD!GUIb4UEa1AcQXP|<bR_IF z?iX+hnm{kYBy$#&;W- zuP$bwQ+Nc`h!2P6@PzBf;9k4#Pj7_>lkcyDJ%?wIfV7Ju@TgO% z@ZWL&a{PbdN<$^k@RJf@xG!-1P*`EQxxD5&4%ZrXbMa>Z9pohf9qMJ{TCZ$p)HQw| zt`6&UXrB%u0{N2`(m-m^34SWjX?{}Bt4=!6PXWmx8R!)#y$Tf%RFDdgj4I;AO#lj4 zdKIdIq~ZnJRC8sQg8U@-`Re4wL79YeZ8a&-i$qsSZ(PEE7px?wq^g)2@nxj=pq zJzmxT@45?kDgO%mf|Aj@644V_-B40o7K!`c0t=Xr@!&&?gX$U@-K7zR&=c(5dR-_J&@d zg-TD{PN4frN8ALsi=hMlw$KJzLkoxq=a1gZ-H1R{+;^c0REA1W5h_3!C{D^rArG_59khEK^E%_3fHw?XN%)nA^8FCMWX3Bq)m9f1L61_?aMjj&;Fy9d zr6%J}1uGG*7N|>b&ye9_+(obu7Qn}lo%oT&&BZ?lWct~-n+cnRE2XsRju^%t?LXOGLcUY$XvYL^q_t zxJ$W~mMcKh@p9Z{jz4;#YTxMB&L*1m&VwqjnRqF&38cgZP}@t1xoUrzMmzU1j}*Ad z^{4oy$a>s$AVpm3#8g-$>Bhro*b92P*#`SSHFY08-%fjo^8H3ZB&Nh;+gi;HrSPGgPy@9j4|~_%_%Ix)aL38zMBk6tDw#7wmN0W4K4*2pon(a1ai_ zei%lrtTh3CtqXKZK7oHB?&rACA4*Si{Uw}*GjJZNQ|1MY|1u=_jerDkxp_c*hzD^Y z4tT&H{NOcmuiz!TfWP4{_!Its-{Dud1kd3a+=pM_XSfGX;TAlAyK4U{c<#X0a1Cz4 zb+`;)f#MaeYx%D_t{Z=YYt5LhoYJTO_u7@aO_=ggxWX)d1fFl53u!71-NKa7HxNC+ z56<;N-0$H#s1HBE6L<`d;712HujmRXUxmw!a5a(p8_0BWW5bPiU02S%cHP+W(u-bt z4Nb43wNU%(T~$|D9noE@={+?!T<@zzzjh0C-PF-!6{aB^NSN}|3*=gIh2bh(524Ko z*K3Zs_3Bv&o_vr2(t{G|g*hLt1heDUdyu&y6X~LFq4y+nfGevveHKw*7F@le zlGTaJiCY+gV1T1U!3Zu2Kz`7gPK!b>6y$m#nH0h;3c;YQmLj;A%?ek>Doh2bz;M#3 zwt9WO4CoblSE-WtRRL90l~4tWK}2!>sHUN~uCN3(j`cpiv{iZda0t46QIZjP#0Q~Ssh%xI^rgloD`{zTMKH! z2T%j_O5yvUH$bX@n?~~T$K+O5`$rd425$>1EVc}lX%i>&W==spaO;z%UNj}TIdSUT z;n0(~9*WoeuRy(OqRiUhwt{BR6!cnRV`v0YOxtjBTXNk3nuBCCz3RHPbFDPtpvN@j z>&h$N4$vMnX+^Zd(-tHw!H&3E$#lo9g+OOqEj&8m9w4)>xLx2FGTm@pYxTje_SX4|6v zevKOWBk?OfH}vOvIM;GV;A*su1HJt@5k|S~ufWkT9^@YjV_*WfVRBvnhxjMM6wq*- zhO3Iqz;%1RTQS!n>fC#fRrq`O)tT$7{k7?|#fgyFRuZuSmcdf^1eU<}SYS4;oADz2 z3t<7w1DRU4-nsY<%mG*SWBl_$!+J4pOXQ?@L{2W2Ll_0e=ZVryu!b3E3%5^$q&*SPN%yaN1oP`ehI&&eO(xAs< zea=}F_cX4)Vm}4?Ec7mg9>CoX`ow-O=#%^1pil7iDgIhy)K{dWl-fjCcaTyWaivTr zT(u^AB+az(kQ{eC8OblDw2P4lHz6c|Qs4#cWE98M>!T--PlJ00_iMNX zH{k|chih;ZuE1sZ3NFD#xS$?+4$i_EI1OLGDL4tAgZ6ul<9-Im;3yn|J+K>g!Ka{I zlMT31UW?a__}!hAo%naacGw1+VG~4b<~rGXy`Z;US5A2-z4AY-@$Uv4aWd4k63g_ipu(anq-(`T57Tazn&NF)&43j2uW(yo z+CO?GZsIRVpe-_8yEQpaSWL36WunK$6z1mbx>i$Z|HrMRYT!o3TkXq0~Tz^mx z_rraS`wHIf?Jo&akADGw!(Z?x`~km%I|6=F`(GeHz5iEu4(g3Z;y`e2zv+*;9Iy4-)Q{5ZEAN2$vhg0ZqIg`DY|@2VQ%~* z(#r4lALSQ4Z%abCb z`;{gheM7GLkKzl`UE&j_bw%_%*&XMXSREmELWw>KT+=HP*RLMz7N7@D^=4hGce|O( zl~4gLw4bBlqE7?TK_H}t07wOAu}Df>4J9e0 zY1l1L%4vSrwVF=x@;kfS*67kbLV37pG<&Airw2;l*3L~RS3^?w1|^Vk9jH+}{5l`b zNjiP?krDK5u4Md(_u-$TA>Y8faaiysR>kp%DVr*ho>Cq zw=%-WSc{E!@t22p$Uqy5N*o3ip%N%wvXYT}W86BRFO#c6QsQdkHiQPC^!1@0)P%Yq z`5N(P|7v*Nhw7k2T0GXmmEZ@^44Q%pR7U!YNCmY3UAK0w!*Sh=Tj5uJuCnd$tM<#F zBP@XK&^3ZTT9vRAvX|eu66gY*9e+2+m7JE~GV4K5XLawA>hbf5S1+H3tHPw*T$lq^ zo>)@l&4zYVW*lzBEdHpl5zrNeLwitRBSA_?IW5fwKp*H0p=74E>;-M<3xQns#NPvS zBhso$|3z0;N=gyc+$k@@DsV6XDnOYlQ#X-YJ0()M<_h6vq*_QR#q|d%(hpbiLh;I1 z@roOWtN6kA2jO;9`wzi047A}q6j#H^J;_ogQbw*!r;H*&?{~<&GeB_~o+?ZxU5Nx6=>|Q#$6BV;8WND+aW+o z;p$)^9&)P9R@efYAu-`QaFc_qB1@<*xX%w#=ySjt5pQaIf`Efj5pTke5_Sj=UJJq|Lp;$=Y<=K58JI_Vd1M(yursEnQxco%NN1yI}H!o3OC zL9^UN+-q*?S_^{gl4i~#{zjgwx2(EwP z`W{G$C%BK{5&Q^RR{encJ$wg8xqgWI0KNs?jPB!p14=K&V{#*6i}+u|rL=~tET_u2 zR@Kc)O`+zJ0&bH%BkUKo|Ic`IDlK#C^t%}*bnkQ;J{oFn25uW_*r~b3;gX5o@%`l-5nkOIP~WI|joXf#;=MetXT)Y4`X0?gYv?7%FDoR&O$u^#9T${d%Px(SEQISf_VgsI-`mRw z`X*NSMWn)$65fHYNECpZj)>H_T2!XNwcb=9({%XL!VJO#akG%2p7r%(ei^`hw<9n9 ztdJXWf_~I52S_$MZZ60J?2JVGLtAO79SqgFG-x5BPNU8xQ_n)N0=Uv#!%E8QPAIGB zhyFsL7=%Dk2!b{<1ymzQ73oG;VhWO}&*FD0b_%}=f0uITkP+5Tk7?zi zU#-zyusP_LYV@$6rr7~{w`U`+6je<%8aCioLROaf6n|RWA-HQn-%2+nPQ82?%m7(u zIr*r96Y!6Rg_{4z;8Be=jz{B;f{~zp5CQQ?&;fS@32v&fws79pfzYbyJJ`*L^V=vq^Oiq zhgE0YLzv7jlXu0{JffSFw&64lcOgt^RH+EnM7>)XcgF1m9YJrw44|`U{ObqZdP7g> z0o_62y`T^Dg?JR!4|f0z1U1EAkX7WW_ebKYygVS~P%OfFF+js@EDR-Z9BwX{4pTuL zNT+1sDA*f{QIjczPlib_5k3T^*Q_`V_am4Ivtbr2g->7!EQGlrh3CYl{S6ljKyEHF zpN~5al9JiSxQpN={>6^F8g~_F{kIZ#1tdhV<+#O>TZg+I)H&75OA)4JSqa!q_-5Dy zIuqWe4z?BaNVNrb7ibRO>9~7w_kcT;PT)TVDUeYU9mRhH4#Pn>0Q(^+wUf*t{JNXE z*J|d^2>Tq4LxhA*1Blj zFL!nb^e%`SG_Z4zzWsXk@SC3IPT^H8zAx+XZ&@^`Nb#V;)`zv}SeX~S-01UT)2F2- zrkL6E%$qJEIH+h)Feauar$V5bcmIz2g+3XFK;fX!pduK7n8d_P?9*j`i)!B=w=ykE zp4JntVushaytLryiY*n2#?080Wy@*PmJig(|z&eFyhwOY$_k>VBGS-L+iA zP@N!hq+^*nH4~(b=+?7ihn{`9`_0<8HRaF=!ELN;EnRx{XkV~j=kD!$o@_Yo)V?hb ztZc2CsZEko;1`eMHLj5BV5pUChze{*Olo5Odi1KtZ|e@9v~t79_H&-B(Y{RE+!4uM zX7>1#RnQ>DPt1lYs_bGkR7u6eu6B&dh00Faa(KHzJV8ZG*zevnruGnT0-tVJvgFZYISy-ayae_-5)5Z(y~5+q?+<0mZD}c1uYfb;r#9>v75Fd#pUH zPSl}&+fJ>KXnJ9mcS7RL?T85p3N}+qBuEoMTUzDBZortdr*l$dhaN#aT6b^nSKybP z-5WmG{lw#6Bxq33AWG6tCh>n{_m1z*ZLvF?#?=T>O#2?K+jMK+&Tmn%YDF7IR%`Z+ z$6vikQC<6YYu^X=NNCWgnY)K~vD(mT_qIK|^;FD)TUo}}*%EI(F|@6^Q$PJ{ubtWt z%Aa9)mP~`Irc^<8Zp{;$+MN=l&WuoJjGqK;_Qd;SdEAq$Hd~gljJ7?o*;m~g=sSpn z?C%X|6)^PsiW`xjhpX-0Nn)NN5x5-*)#!ZVwddY{zW7_aMn(P1p>*E#{5$FyGJ1=7 zk|i^BGkR-yvM1ZQB%{~k@9CG!EYIZapw>&0nR3$6d()H4fbuhkl>4)GTw7h5*{^7F z6NZE<`Nl1s!c5KVogbQ&A()DkU(V}Vw$sGF>>gqnCxjT)?$4yzwxGvni`L*jGL+ z>jY9P&v$!L|M&Bff-Ydy-;wmC3AEd=*1bx@swF9PJnEdkP_`N*-T?k1*$nWV*?d+(P zFoWGJ12=v7?ewkFc2X!ss^j=Fm_^E?5EAGfndfHNX~k6XKro_~Sb($mw*XL>F12AJA+ zy>UI=eP&+{EH~U|o;>yjnkPA^*?1p|QZl^bGl_C~^LfVmOzoTqIc^R&{vJZ=W=Ynb zJ69_10-B71i)f0SnaNC6iZz+c%AC~vKqlJ~#rJ!6?)BCB+%mT{Eu76{o<}R=P1$KP zn~J%-l{`5!zbX4^E^iidJhwNctB70$`s!w}Es$y6%{znkrvBc_-fHh=S+v_TP@W+Z!=Bx-N4#}6uba#C zLLxLA3C$My)AstPdA-5R>Wrhp8nxYtQLk+IN4-@|UQFxf#E|=dT;_n%PD4VS{YmW{ z`&%6ET^$K$l&&O3E1g{rO7>3p^S;Z(Xx_)l2XmPu1*llD+@@CvZ|ZmimCJ3y2=dip zb;+oX>=ia_-Jyum$)Z%Tg#$fE)-FuqgSpM3gSY{C%%&jRPx6?X%3*sRJBOvo=I-u4 zWp9|117)1aV^S1EvPWK1qY&=tyyj_Pc}h1__zn0EQiFh|kJ<^Nr*tB!eE!ka#}TF?>Q?A%&7#hDR> z(COb6^(-!6&Z@St+3)S}t_97xcbQqTRAy#zjl8{}S%lGjXLzt8eK;`9*`J3;HPl;G zbG7i5AcY27vamTnWEfhnF)7re=%>|!%sw?`lOPjT64x=3uNNtF$4VY|(Y;o|zDTDG z%s4K{q&Ug=iKV+Bktd&Nd6K-`MqeI`fu{$VL%{?t3^JK2;jRxdFDl^f3^K`z;vNk$ zd5SW2F9w+; zWtJ;g*eoedq+@&EdfHLz>BQHb&u+XUac?UdYdKh>$ed6F>BZB-C@%qJOVF?6^8rh% zOd1+ho|9E*DkQWJNj-H?)>qfg+>VkMLX0N6A6jpSSGM2Eubdd_IXc)ZpcLOMB-E+n zG#EJX!5rUjjsy!1+WDOHSRQN&S7b_~DeMAM$0Ln9#@uTCAJ?(f0!^Hhww0Vt<#B8< zFvR%E+O}X?jhZQ)5gorMUB{iAoB?7@HlDj7CW%IktCl;89Cdx=>2mK-?V-Q_Qt)uA zl^v+IGn3aYW~P$T*AfYJk)XL*^ZrmFo4Tr&|4h35i&?L z`APlSMU6Y{Vm_yyjF zT$+LG)Xfa5=uN98vahXsk()kRP&BWbn|&X$5@6t`hbSOvNpnauyRC1UxFLQqc5ZiK zahvb0cC*{|Z6Q}Fw*lS6Zn%3}jB4z{e(vmT%cPDM%F^GyTbd>1-rOzT%$Ck*3w90l z=2q(@^4%|O_k&G|uXi6bcE?4>r0gxf#YiGasM0Y9*L7%^yvlPQK&bP#B$VPOkCXo?*|qZ$^rlS>cUUQpZEH zx8>d0-s$$vXpb^rv`!UuFOIVMo4dr@`nY!w*C;V0aqhrQ+gRGUcC(v2&=5TNW8l~m^A0p#!$Y}sgLpGpStYo4Ial9*mKeZ{ z64l9wvsQ3jx$$cTo^)bF;wxuv|LyTVTfu(DdOMeYcF#Ah;NI>%rz)BQ??sKX2fs}^jTG`UEUc<%(*I~Wu*;XQMQHGyybd-jBvXJz~h)j!xYR}ag<);e?T zdnTYNn+T8IGsCJP`8$%DfbSem{c6nBPWR)oqg7PXxnF|HW)l*jzRIYN#K zeCexm%^rNaJg&8qL#6y)S2kJLg7IajVn4&yikl&0_ryn9+Y$_DzY4Ww<5tKS!8(qx`B`C@2wdn5qpf2_CIoW%J(?> z{D;PFono(bv6`lSb#9T(89rL~wdtLsY*^ytC7fxNaUA2embFRAwpOAVY~JjvW#1Lk ze%1Kru!W!RBW-b>M={xPVl*yiX83j4h&F}GS>3(}_i?}Twanxi*z!&-Q=lfUUmbIZ z>(JD7?B#aOyjAZF>hV=PJB1#Ga}$$_yfU1aIJEFz+1y7;N-0&xB>RB0b&+@niTlq> zP8c`v@gOHHy}zv!^ZMC?i4%T(_pudYJ&yLPW15rJHy#Ns1Xi7_S$v`2oUKvinmT4W z5}s9c%-Rol2(~q~?gUO^rxL*)UxS_DHQA8!HEn3miigtYjK8;4_#JZ7P9n8v+t7@t zg|6L@kY&cEXx^>c82<%!dD_9JYi zMssST(-)0Q&Dz9dX>2;x_EyT#u8H0MN8oC9etK+puLW05>*mS+m+T+X)SM$l=!Z@1 zd9qQROCPlO<>XyU59@(Bbu+t1ELuDB$26%gj3uT74?ff(TQifX4rw$Rr-3vN{YeK9+0XlTuLNVJ@Q9_3ruc|B^^w85=1hblR>B(G{MOs~4= z5&Ou9K@ZMM96b(@R(e>qjX@7KhIN;8C1TK{q`ftqvy};}$71$=xan7q`T5UqbF&_r z>nGf0uQCf-^iMo+TtYNw%xNbweruDWJ}K(gN}w>Ebq61Cm4 zlkJ|W{)yJGgRdU5rL~D{NDYp*Hd8;Lw2OqRy$a8tc464OYC39R|Iw;JYjYM!-!Dk0 zy_#H3_gS}tVT1=rad z)fnGzIWcPMCaZt^ywWG92YUSH5~AMru&r6th==Gw?d-HEF6PPHJx3$<zcI63!47t<0tSu9kYmXmM&SrakjKx& zsI-JV%N+lq&iuW^6blLt#k&1EnthF_@1NmzedBEG5HK+3tVHClHYIIjNAnbk(5W5m zx$KJz`P$`qx0If8IeC)A5@OVLdGi0~BLGFvB}&5_K=zr2_^`N{T37O{83(Blvi>M<{xA^8K6nn6F= zQsK?bhbt z(rIl+;!O&t1>fYor@J{#4xaPfO_^5M__yx%`r?;;x2}E1UX{oHdqOmF-Y&zjG3}G8 zhgr~qJc{-(HF16K_pojK{)8$$e=WR3+ngnIvQeU_V{4C-B4%4ya};`>Q=X1Z{!g1T z!h70%(o?N_@V(^mT2QL`GqaeZmnUgw)2(Gxo*q{lSMUGtmWiz_Y~k23=9)zxe{Si% z`@L*y4XeI3DD9_}wUOhD!{54_1GLGTIW_hX>yA3N2mRX1gfR+3tM<0nfQ#>^9ov0W zPmc6-?t)4DV(-Hpdz-0ByN7nyJ@MmS$3MvU;Q+0UXhn_3^TcRJI(3e#=_0#L*UX_i zD=}F3rbfTrde$lgl~S}OJDpwXX4m;t zJ2cIJ*6eb$>uc7Nk*|ASJ0s7^r>8n}+QsRH3Rj&5_BD4@)F>o0JATn{&Y26BMzzvu zw|(05u@h5tX4@f$7cSv>cq$?4-K+YVpf;r4%e7{bMU6{c_8e`Si8ZlxCn@1~o*3EV z%BVD(6J9J3H!9}4zGfI{efqseRpi>=zaLNZ_?xj&5`q29CgolP2^|1*oYMc|;kqZzg8IKOHIRyF=8DyCgO^QsMZlo3dzK;poPt)Er>bX!%Po_M1{3!16@OpUhQ z4!(dvcD*aFeZ9uFxngn*N&nX#=;cA?Y+Fve?hZ14we@E4tRHO7c1#fH*)iC>>PdU; z9Aav>^X5yGf0#Y`1BRO(XV}$W(9YY*_r(a?;92(tJgoJg{1UQNN5JA=jWAi-Q?0L& z(Czn^&#xy8ZTvwYB%Hmj$HYkK_wJSbY~O-!?DKf*)b-^E(?ew3{{j{kDm-eb_+DMBNxL+Ix{^Q%_z_ zSlYq+$dh7>ncfj~(~YrLAcK6J&)uES@)yK)97d7Zh*1aMoHWz@52vgtXsK%*pBETo zmQ+n(Z67#>_k}vb8P-I6z2V_@KZLbN#HL|mOo2|+E%sMis*f?@NQ5>a4{OoW^UHZN z(he`C0_}0w(TQoYF8dEj#}1H9o!epmF=m5uAB#jPl&$sW(e3-|)tO|vbw%%GNN53=uTa$&e>4k_`JKsf&SbMh5-TU${pnzVkFS?*5U4Y9#)#Sz z%U;|cneAB9aQ_f9fRp#^!nyg=UR&=M3cn9^{P2SP4 z^UXy%R#ay6o*0c{tzc(OG&jlJ<21YH*>sbl2iKg9=IcRq63sAKo@46)J(wgj%rNT} zd2faZ(!hQ^!`#vJUo*^KJs9Bc&a|fpCqDGzNA^ECtwl_Y0$+%%u#hvv( z!gS7KrJbUXwl=rVG2bciXLF3NH?M09QTLuBM@|FPY8DV~i^kv3OPYrxyX!`Xc`N&2zr+Z!>Bk&*tP zlXF(AX1A$zNak)M=PYEJ_oIq$@8WT7Z6`e0D@l*|i6aR-rz{f();dGcPj#7+_K;q1^Jb2lWhbhXCe zjwR;FAQ~;Csr_`;q4>bNu2q_lo{-e^nA0_92iUO^u$e7@v-#NKst8L3nk|Csx$^Nwe8X* zUC-9Kr#biO-s?^65vnc{S`Ga5TcUWo{APUNNMN!h>&=)E%rf2$_Q-hBJ@trFh3egK z3_>+iY%o0{80|ldU;t%w#Qa{I>M%Ucvab-cx261YZ7^973+?GheBNg9p_QrY)Ub-O zCcapT2z^sT=pZNU%-XA;t^L0LAx8;XWiK(ws6g8@nI}$a5Lq%R#!=6A5{cwUWNC7$ zZ$P(ZdhT{6jH?^WQ)w1M9`@t5U#E?B?m3f<8ks*%`9-947Fy(9E|TUrxxqA!qp_a8q;s2w{pCA z+u19z#&kK$i~9c__-+QVz4KK#`a54~OxP&i#9&IQJc?f8z5q60huJU+b&u_^)eUJ@ zVPvj?xmo_RH)5%KeusH6ii&-^!;Bd14XmAir#%cyZg}_I6rRb`JpO-@mhKQ4dmgdJ zIJt(#p6jz+cJpPwcJq@DKaF2DuDw7H4e<-xZBC<+ujOw0G2n6OsYi3CX#2B{26(rX zyUC#4bH>oVvk}NH6Am%`&Yn%5ElOY)F*?Lan5XmmaX;C0G%DuuZc|ffe?=k-5)Y1V zo3=Ckq3%%55sMLX1YE-a?vjO z0=wxUL7{$|4w&g(69i6F1EE|o$;1_|ca{-C)ume$UVL=y5a z#Xh)mqC`?{?C7oXprXM=51MS_G>aTGy~a_68VAipUB7O<^XfSE!Qae&lbkHdc0AW^ z*!V6v$Rb$x@FITZph+8YP=Y4CFRmkk`#V;RSwzJBNJWkQB*aTeEE z67sEWiDC>_drLadGv>T`F^y%CdyNfGPsfIxFW5FrbgOcQi1goXe zwNA`~RhxGX&$=xgF?z3<-ZXW&SwLFf4kXmyt|TZCaCuO2p5Ewh8n}K(FPL-6{X7!7 zr0!Q zSyu=T5u{Ca(Ozm4ZCk(RgVay{BF5>(884Y`r1ccNWX8>;8`isOo;cSjmU;`CVjp=k z`(ikn(Q9mEOt0(p5y>xIGZR0eFU7fG?#%WEWYLz%`#0=24Re0BvR>{CT{v>mq*{z! zjCME7(|HV;9J3fQc9KAkGatv7z=@mYT>J!SGL`hZa?`HUk6nh2OIRv&aa1M`Z<-Y4 zn3;@;}y=oymUq?nC9?uL<@sUBY(-AuS4f&El!?Qwh_VP?*zSN|hECawNa;;Gx_ z(0q)3|F(S_`*B_S6^kw`-Vvps=W9Mpr8$PpD$(29eVz^jo%(10+6k2h5@53DTKw=6G8rx`ViS&p#B6s$pTj$6uRBLn_C7aZCE0 zvuD=ve1J(zuz^b=FxVvcMq-uj#It{XV1A`CLb}OhDqVy`kt9Tk1a&k z5@)|Lg;P>%x5r@NC0V(48{Cz1I!I=o&ST$N_(w}bS>5Ry9{&esQ)2SATk%a9zSw)l z+mc%!+K;cl*O>NGk^9;09VTlGxn;QZdAlq(-p%o?YtwQ84=L^X+Ee|8)z8M)oxk}c zCx*O}qNm1V-(|eWP};zL_NM8^h=}h}Cj4n`ltc{s z*iNq1`kB+D^{{LGU?CmJ-kT0I!Hc{#eFG^{H-i$@PCW_!IQ<~{pXNR4SmKdcu!u2} z^|8JEzSwWV_8(VI9%o5dyKVOOE5V-Pk4@ag%#0(Nm>MJb0;0=ep4cjpOc@`^)L`SM z-cJ^iOecff2WmYwOBR#yzZVV^&3%UNSgl)_d9;}AvsRBywk2e4e@hbR+d#guZIFd#O@ID_ix-*IKaAOxOvR?+<9u&FU7c7ADiP#QL{!o6R?bVvU){x4P^T|W2TDl3h(3)r0)Z3rw%Gd9k!Dh(SEqlbrhA$)iI-1t_Y>~bfQ zC#7cG*t{y2yBuYb+VI@emc(Hs)U^Wh%-mM+hf}|jiL-1!{oIUM!KUtYCyxQe*Tl=@ ztv}e2z*vi&m0$MVCz6d?^}=2zSf7X8e{LR;*7M}K$+8l||NY$bSc&1||7tHv24rl% zqu;U%TE%G}l)O?CqnSAAm89K@pSo3_7`^a<(X#$(_N^o@8sI5;c!HjrM61|mpG*o3 z{f8;vIXE%nULD{$+tGhIG5JGYSMM9QJI_LtVl@DHOkPD^&dy5=_Q}VDr2iZVPxt30 z*=kC4u1(l#)*#1TnCYwOm}g(u^VE;!4lT|-Vz(BKI*LQ-8!yallKH+zLhG{?gQtyu z*{#0)EMc|Y?=MWsHQo-PHDB7>NyT!^sG8DeezvNiapTw4iD}aA@u*sf`f6jzbh-NAPlpt6+&Z{-*4wWL_Ch1ro%mkEgdnoV?Oz z>_F<2C?D8+sPD_`g-b=H9)#>cWYxM?KlKK`DK_H`rx6bxUES=&mg~2p(l`Yb$+$M2 zzdh!k=DL-ycu#G;VGwPo?vLkhrlCvm^N6IOjUZ+0l@h?P3 zX0q#un*q1|W#Q(t!KNLoGcF|a3l%_7oz?tE#=T%wf4NTx&b?t~lt zHrG6rz|zpN%Py~Zgrx5x5}AWSIo-6x|bH`HzoAAur6!krGJfmt!*M{ zK`T{CYMPVQb0Mi2u${?-k6PEt?Uvl!R=8smlVS%Sd2UQ@a_nFMO*2HkCi0KuCVU5O zk`!jh4iZgGX*O^jYEs(Wrfb<*6MA2~`8mb0kE!Bfk2#adyigujQklX#$@5MsQ*9^L zKcq4vbZy56`tGE%^DZ%`;H<1iQ)18&h3)Q)#A&7Io@UN25)4XXx;03U-@LcWn>DmM zztW;xVWCy!4m}v#hPNl^$kyFCMvbhPhP*_ui~M@<`7s&#Mg-dCO}wpE#tiei^(PO$ z4OPeCguS_T(q;sjOuH$1VW26on^{KlL-*ZGJ8J{YxZT_-5?spQuYY?x(oA2|!b<&_6u-p)#3T zCqa5MU>`R^`?Z18@k;W}UM6z~WjuW{n?=uX9p&8oP38S8(Sozu4O4YRj}2Ed$Jd>i zkJvPx`F6G3VUm@}Y9`CAm(^_A&n>PsDKyS~e?%tUc5uCxTh3lW&#dOvek9AcFo6eX zf>Bvb4SKq78Y$F|C*?n%etEH4!K6?>Mnz{X^DM|}&V7bt?4QhS$ZF0WVDs>E^2$YC zZ=WPO-vh9|E&AqDlea#V_{YZzpzfJW z8bC~!PF4OGc&GFpE3MVkb`#g|#d3e5p+*Fn=Z8_+?)Cwu%n@%U9d24PnbUfn4!O+q zBUBzEtUZFT^LeAEUoP{<5$4QMxlE3u-cp|5bD8j?$ob_qLyp>(i0NZFtG&!$M;TYK zw6@)%Z+$Z2i^Y1;x^zHZbNUzxxLvzI-Z`Jqvn%n>>1e>EZg}RK0@;sg)7>$yNoi_I zVwBU9>)#lXC%>-*lX@f4q#%uB3twHQ^ZdTLO^q?vGDkXja0heyc#L0qTXO^FOL325 zr;d3|;p5y}`jNNpWJ?d<8=ULnRNWc$Bv7P?-;}(j8Jf6~FnU zm>bXflXMLCBPMqSIc)e3Yf?* zs8PQHX8srSm>C89{b-0t!p{=!+gZ@Q#jd(?Vsp7Zi(6a$)mrH|t2UGVG;4?>1x?Ut zviYi@se0O5-_yI0S;Tedm_qiPlDOIF{>yt-p3jaA(-A+)w*f={Ktglbs#m%0d((YYB}yW7VS5R; zrQo}3_x7EiKPtv~lhEBj_O&Q#SFp;VmS$+`{5rsJcKg}#+(#+CfA4m6B8_fX{fnBX zNa*de7p=JSXFJQC-`$z!RmMJNSrt4BF^A5gY@A~L)>gv)$#^iQL4bA*TAL=La=ebzCo7IBClkH@B6x2@+b3 z+^fAVqGXFK_oF2G5~Fg*u3bFy&=V0{bJ@`G-E-BN zZpBz{a;_+8YLeEs8wuTJZj{?uBF(T})}b?Nb5C(7-Pv5<~leA@ukvIZ>ql=zVC@JhKq~AkYX36UR=fSmAf3r<$ ztypZ$14HYSw%0T{wiT;UerH^F?~gUjN$T+qY4d*l(=YvUEjn+NWIX_(%M)}7jZv3i z=~9}1DNK#u>Ddp*Mi*_wX`53G4leBn#kX_k8wNJM=poX*7mt$v%H5LSqt6n4F}9es zZAPsQC~ZROE!|f&uF&a~)pm|FqLywoU zXU@(aKWVghTIt+&-_ZK>zcjY`m8*~EyYl7^c}Y+8Y>pxe`#mdf_t(cibPvebWz;?8 ztF=&Jzv^Ko=nB$}!c5gGOvj-W%$zIKF06t%rE8~_9#_+kN}3v1sZ;FwxOR!LZ&`M6 z^Q6{il~;@{YV~nf(k-3oyzp%fPHSFcRqI^*ukJq*|KsBAoch@&Z>96lL612~8Z0uGB1i zdeh^~NbvZn=l)$zOue)>Pvkv0;*J$#O$#S$n+0D};`Q2Q$JgF`<=nU1`q!~{>z;)d zn%wN${1K$k1`39ckQ}nz&wd28!uW+|1n{w=#|0Ckgvhg3{4;q-M_bBk!2Idis=ZW9Y z9Jxnn|MyQzIw?)XZ~RA@z*vYX*J8UZJ!0cEe*+o=M%Vaccc=S~{nOCl-#J zRl4=j7O0F3RYT0t<~~+j*4S*iPfNt`b*+|?h;C?X-+69*TP~nQucw`zJpTW?#cnc4 zK~ru$yLfA{i5GP?rEe{m2%P@yi@g^+9p*ne%1Lrp7rMVTb4Z;kM_6OiD$?68XA^Ul z&K4Sbzb@3o?f_5odOOUUUEBUn!`h#Y-lG4}x!elO9%rA?uJ~|C_Ix#$YJbR?KXx`T zV;(RaId=X>uYJ=+?Dxpi7~ey0?U*Hb%_gK)Fa+_WW~m|~j~B`%t+N2nd6*`{twlj$*ckse57A@}s#PsSg5 zqRz{xv{PG}!ap(@Z)|D8(KOLUB&CO~dA|SgI?$6Z+=M)eHNQZQqmillBR}-`--W#q z_WW;32EH-7^Q)51bwGTbj61(LX-Yk@f5@@s6Yh@V+L&HXXs1ls%)}>*`2W}1cZXGV zJb&MNMK~7`K~X^uh@fJjmy1}4y&^VZiHcGMML@ugfF)K`63g1fn#77mMU5gcF=|A` zo`fPX#ug>0Ut1*le&*~sTtse?-}Aiub2(>cXJ==3XJ%(+_u#6;pGr;cQ^hf(1-jYn zvoKcpCpftyj7#?1th6q6LEULKAW4z3ISz&~i!wm`00?JrFyq9wgq#;=atMO^8lQ$S z*C%L270$Bgw|+RAT83GY8^KCYQ+FYP3xO4DJ8j-~uE8nPqyQ9_X%R{+fiuDNsb|=` zAz4xhWRJUw1&EU zlV}BH@`)@BuFY-tKi+OE3=r|scrMM;2M^u0dfyt_2NG^9DCNu%H&rD4YWf^C2gNXd zk{Pn>VDZlpQ&0rSsu=dmb3~~z03%-j{oNRD>>rqO;N;u3wMh`l3g=l2%Xk5jg$Z2d zzUa|tsQL5 zlm`G^Lf!SRk&+QSkweTsH>}*zvBNV!$epgXu7nnK?fwG83`0~~BC%4g%jW(CtL9+> z%LF3vqeQMOYMM;U_@?7d_!wm)REZ3;M}L_(SLn0AB_s^Q)SgK!_-{<}!;{$Oe?vUhoy8#smU zT2H;QY~^+Bkg?2t3|+N0gf!nTM6iH&AQ2{@6s_e3sT8$6_zsJ&WID4*K-BWl^pyj! zaLT81zf_(1$=bjS=N~L8axY{tr5Rzra--m>)LZ#b$S3poxEhtsnsFodhPhvC-THld z6McL=$=CggA0Cg{I)iIqBze20fsXHHFnIgAM%DnffMnLGBYuOESq7fP>Tg}9fY`)jR?-lV z^O9LHAa(1|D!E5T8(Ur-5M+r{v`CMZuGBt+5^_(T`Q@%}kkJwmB)Y} zH)WI6p>}s#?uBnpfv7c;jRAzN^-R924%~5jRM+1+LrNdG{rJJ6Be)JhY2P+wl`#Oi003dFG~Lh5&X`-T>u6Om$K*k${MbN1 zYA&R(ENlG!lEPx*@TpEq9i;`EggrsZzVwtWWX8|B+pw55Yq|Zm=dQ;q3Zgek7 zoKVu)W6FjV`AvfKCD&&$GcCGZ4hRY*I^G&Obi~BCkMt1DX0uKN5ipxWJc+w=_Su@> zZ*T}=ZqjU)D+*S|-tdgBpUsv6TK7GmTLIsa^X5msu32DHeerYa?E zRSo8_LTahg9JLB5WNDG!BTW3%-M&yDd{yJ-Fbh*)cf5r!WdR}~=+UeBf9pQhL##mE zHYn+SJJvJ3$ZVIs})CK8`~RZ2Sd#$-=ga`t+P9#ZLp(uP8t{vbi6Rx>PkuZSfYLo8I< z)?+XP$&YkcYu=`^y_Ww0vwY0pKlIyBikE4RIh3&V z^(xV>@K52qmT^zPPhQ_-Ux~8Q=xZ9HQQ@CbUS%p8!ZW zQAVv29_jsmd=}Yy?f@$q{rjtuAHHq~j$nC{3|4w9V-iPW3qgB>25%j@pWJtKsl+>E zh~i`M#q2%WxwR9i*b<5*r!n2gl1Z6o@pHB;zl&g0q2cRqWItL%MvU9Y-U3K>rKGxZ zLv;4&vyGhO1(S|9*Yq-(pixi78wxSXB#S%nOdDIx_SfFC-37YHhW1W4gZkY@mSzL& zhR+^VZ%5EF4oPJfQCGvQ20Dsn$U5+o&qrZvzmdiIppJSI8_)>9^5PO5H*q~bufX@m zmX_Dy1}BkDt@PN$HUd&ty}l=iXw-$BotCO<@ z;Xy54mratr&xS8A^#9@$ecfA`EFE<<-0*0CQ*hkCz-fieYRO2M8E8whp@KV!kq0uOZCPIK@8o(#sEV1=@vD8(ID;K>+&v*B0E;%<4u&n zWJo*OdZq23M^+rrs1M?eBi?Xx##$$A<;Kj*0i!lN44Om!bb0)$H*=_6Zsu4qizcvN ztG9IsjN3Zax^`#+=|E|vS)UCIFtR+YZz2IDekk$xTQH{YgSM1`DL1}rD@$pDuH6TO zKOk-&opJ1W=nkBGP#{`t<0WG*HCp|4&*@52JbW9=L*43(70C`zauQ{|7ipGstpU(MY1}PR^%{u*0Ftvzt?5z03^-vWj+8YHBL=Gv1s|kWW1qsEAl;VH1+ZbawXo=UaPm|@@{b@<+kKF zfhNEoKPf<#Q>UT0p#&ZqU8am=aN@n=!$$d0P7k6KRI=|F3&gnsT`nN%61vG>fBnUt z)TjywuPZ2_T&DVCEs6)U?vt&QxT_xGwiMJAEx%Dqit)@Jef~c7a}oC=5Oz z)Y2h*{jN)%$>QP@+>tc?@!)Wk6>g? z^Qg)xW#oA)r?2?+S@qdR9)hp#mmEGpher+`@=4E0*T6z4EgYY4M+w>d?u|Mf8vWC9 z@``YGuIgzH+fB!~6uRYOy5eEPuuM_o&ZA?Ea_#@Vj2iT%Q#b=IpJY~^LL|=Ruw+jd z`Z$=A<%wm2JDWVc@SJjrMbfW=5ee`Iy@UYK{d*o2%PK8iINxMMcUlzE21SLanCJ}* zTE;edV}LKDaMyb6&^4Pw#q0!vgiQ^L1xxaZH*hQ5R`#y{x>2Yw;AN_aw^7Q5MqG1- zcUIj-==Iqq^GgzYu+P9(pU*IV9Tv$xK+sBWX|u_!&YcE_Bz8#D=b*#_B^H4XpR0?z zQa%VJZ=&wSGb|l-HFwUi9Xb##``y{K$wGY-$ zp^1ir1tT@R&1|oKEA~3idEB2=l>WnKwaiSg!;+&wszI0d;xBZ3+&|xM6NLyV z;}sJi>=IkwPQYh1^Vvh-(75F@yY?8qD%5Du=C$@HuhhFnnw-n5LkGM>)HA*V2vU6S zfmhfzdOzX{^QUORhkk3>olmgB7e%$MMYYNktgi4Af$s)6S z370ZD${4pvW@2ab#P&L$j4c-5b z@HmH?dN~U|AM!tmZD6UdN{LL3KwVng6FVL_xuc5A`VXXhIMI}OqC{u7 z5#GSgOXYo&Yu`&5t;gTH)NGhWZG$%yzdEg3sb^Ne?0Vt7(gS(TDIJkllybGN*sy>*x!UftFK=>olNaWe zEMF8RJ?bTRYsM5Xhu-LDL?O#X^p?f;h7lWE$XC*N;X#W&fj{O+^HPe3;&P^Ky&=YN zBd=K>5aUI~7OLs~ zp<8C0oyCWD)d?H4JyYMEVRzWu01(+antAraZ{{5q*$*a5+V5O|E+6uRy4Q9!Zu(3i z^$4-p<1V|{53O~>No*2!k6wiqjjo4*tO!|Oy32)1&!E`ME;YyX(zk}Q*Hsw;y)7`mXQ??ON&&k@&y~j4P{Hu5C0mT7gvCZ$eBFU({@R6Amx3?7GUu` z_9zfs{o@`p?hh`LjvybdGH;dHnAsnlP*MrxX(e`e#C6iE?I&JN_w0u38k#C}m_nYf z?@d6lJHRbLMU47#->!xlhU}5zA4NZ0w?KADe9tZ$$@a zXp)1?O0og;yz?LsZBokhZ{GM@yN>Ht{v|_Vm|)qB0fe%8-{*$mzP`6=W!5m)!C3jG zWt=EeSF6-sy$2^#N3k?v1mHk__+WHLUgVmaPgvey5O?leH*pQ)Azm!>k7b$$@mR^4aJ=wtlirh=?U)R@qK zcXi0X^MN522hYej!Bu|ZKk|dUJRq<$q>I$$Z8XBmzABkU&E5Yohatd^Z3kVjtkAC9 z0f)V(uWXMsF!EK^d%<$hGhG>+C}ci#{6T0rSU3awUJ+!iUhqEKe%2>8EOYr3eF-$9 z*-*5g{Bmk=%l1oNW1y3(S+EO;0wGrZOR z9(5_X;9<)_-)2sG#sf-Nm)@#}uh?$X6)j(Lr(NT#t3x|{xq`PVVOJ6qIsk%v)kh~h zbL_JEC>fv_W1@XLN+?QlV$mAAHrf?nTBeW&XRa9JY1gQ-jZkNfvqvK&S& zxFVJCMXf2zVa6k22Pk4#7IFrb47qNqh(ArgcdTqAhIhm}uCBVRiafHryUHF-$&+>; zd`u+GYd1gHq1$D*2$Yacff7XCD_t}A52D4u{dyj4Rk&j$9d#?Nk%4;49R;~or6=!+ z(hA#47Jj~0ux%mPy>!%sXrm6lNrpSuONP7CNu^=8f`z`6MDIKrf`aD)BMg+xrsKKB zeYJ+|8!a>xA0r)&um@Q7UHav_I3sjiR?CY0%#Z&@+J39iNE@wXPexf61->%}B&Bd- z1kY~cE~6rC9b(SF^zThY$Jn0 zB1$>2p0FUA6rJfyp8cl-Sz+ksw^cE{vZ%`;{l!T^ye(-G`8qn&?i z%OZUe+kRsfPY_j8DfJMtp~Q}d34xlA8ZxtRA+oxC2?91`&M9}WV5DUAr}^E4Wu<{$ zC7V;W_*kt7!IqRfJ|iTZ;n^+lF)zQ_N7Fs%;sT78Y>OWeSa1ZWGz-}+)!%I$M801fx-O;X!1>g%UdmBj!L=Z+K2l^a(S5iE( zt;!{?x@xYUrSn~|bE>&sn694z z6JczLQNT5_78`)ejYY3(JXdw-p!KS^fBr=_FF8!G_;CI4MnGu3Fk?rk2EPv3Q}eDi zvyVp2zUCUqE85M+@yi&knT0gJs0r0@a~2ehK7V1xA*QX5T#%m9dpmGrUrsl^$(gc* z^ElxOs-uyF#s7Ahes|mE_&ai>>0GA**X=s23=nkq&vcw{Q1jG+<&ML%Y@k#xtEFu$ z-+Eg~YffH@4MhX48Z3%87Hs5rAXcUp>=9gpj;O!E5w6~2MVC~`(^6N>2Q6p^1m^a# z)JV3V@9^lzC!1Ye>7oVce6?z{C3B7eOQryVvN_HfxB0YI+NI`thzv_M1`xWPfFPS@ z=H+)+r%b#QrH42zmuyH;bs4oKbDX}U#FFi$y5)eN+z^*2r<%i;)V!&Ou&K*SQWoai zxpU;zG<}J#E-Q}_){1KyaOv3X#&>J(oV4DDVWrbzn05;9u4`G|1oXl1q?cERvThZ* zv!wwGjD<6FdP9~Ui#ez4uXv&>wlFX>>w)a|@1w_*_o+6PgPdM>m7Fvyj>f?n>T<&K zlEeF=y1l^)lmw!OSF$zBj001uPf;c8!jdj{!TG zz&lU;denbqX+JCV2D!b*_@!a;KD>QZNfV0%D|NnHBKzUR7#oe0c)RCk`zxnDpH5qA zIYzC-ODY*>mGhd|jdqMF@XS4>G@N$U3S% zf{EWWW>$$XEfl!viNb7+g~;9}Vt{Q#E?4&t7pFRD^PWpGK-y_3Q=z%Y0+O(-6rz$u zmX^eKVE-VTX_Y{d$ii7O!H$WO(A-fwjWqlRYUF)sYc}k2Fp4Y%K<3%80h2Icerl3E zcam^iqjZ)o?xNes#D?%=l4E$;nx0Wl8}FpeFX?McTG_#_uu-h$3r;em{EYxzsy#PY zI$14MTh~8A`E#_9gDZKVT=L8O|J|{CdF2%57}bTsTyD>tp*X}a1O~{fynn^Qe0RGU zt)wnUi$_*1z8ipp45v$%+vJ|;tOE{o(wFr1mvYHw&l$TvTlzDddytQ5R>-$%uUeM^ zPs)INZO_Vykl_&DbQz0F->`W}g{KDs86*=iMBcfJxoD0ca=havtGahx4G3~J0YVv6 zIf&4X2I~T#DM;qJG`ywzU*}ruiK|Xg&^{8EhuX8e>6jd{ywE(bUo``d&7=T3PBZ`| z8Au(RXCHXc$c^HNbm2c5Gik~K0jX=yltX+OcjKGO6KhL$H|O^^jk$d^_vQ9GK@Jn| zNZpedq0B_({DRJyNHfUD#fIHn=<=3_y8O4G=Ts8A zFr&825h8UTYGm08#aA@=zNua0&cx+T!F2kAj$~oA^^{`83Ga3&5dOMLN^!?}i7*DNm^zo9&jXh2HL@xuLI(~%W`6l;)Es~Jey0pLv0$grC!tliOEiB02KBKqd$>U8rm5x2+!qS= z;!|I~bC`b8s>I5&{%f=??{1*mViuy$AI1&1DECwu{(AL`wyYF&>06K;7NOPger(7h zJg+ii+ZJKU{qyz;s+A@!-#mG0!rQY>i?NR7+up@`9r#i?9uSvM)y{3}lc%0){?=+HPfvHevFO{)+mfe*M#s92 zO9apM|5DK6rh26V%4ywgxp~N9|5Y)?56{%g&kByN71uAc>V{`BupjRd>Q0>S`mO@Ceo1VY*1mv&F%8#xDOcDI_{(N_1>^&ka^%GWR@CpSV&)7C} zm%U9Uo=N@AYWT7FJ1g(v-FUXZ^J%})gFl+w?q>Qf-s8hrdzZH`G09nnXKR!<9ly0+ zzu-qNzs57cH_B3P(55~(9{wF(ka9W`_IyBbsn^^>JX_+~Up)VA(9?P=ALE&hTJ~R8 zXW^5((?TN><3kcaU|tvhV{dwn$PLNk2+uA$Q2yP3fjcJRnS{`9NwcFZ!h09Yz%$K@ z?c+x*j_lc4oG>vdX>|Og(3ftOuTneT8R2sB5l8S_vCCJUd)|F30VGZet#2{q^0ucB z`d*UDM~-uyF<{{3rc_Qd$F5wv5 zX)XzINg+uQE|JkO%)?qMGP{>rGd6a$&?2>Zs0cR~H$xaATqHxlHEHa0;EQlcOu~op zCh#WW@R7%H5n)L#Q6Y&@NeR*MC=W%cf-5N@BsMWVE+Hu@DJed&otqmg2r#XeDi>F3 z6ylLShlVyfg@=SjxrIlEjE#*;Oo|Rm>=YIk6PJ+a#($y{WdcA1AS>ue42dMtlsD0_ zW1}Od;W=!)3y27dPzXqf7#q#}t+jQWKA^D@ITdufc(XAROdV}1^$lQ^o><8UZFAOo zrq+SA@X$6io0JqC;}RYbpAZoi!hUtp+OlsfwXN8$5vGk}3--`DvUzKSaQ1q= z(6)6<+}Oytgb6?@|L5XK!BdG7Nz>vZ5-S>GPkz(d8Bsskz>Qifn`(QE+CD5~9;lhJ zPH0mnG9oN01h}PN^d+QNXvW553dUlkF1+6%7Y|ovaZ_+)%Z6)hn8SKg2bPv%TGN`d z5wMk*>H@vQvRY{!>cz!IxFkhSpn0VH%P#*Zw6KVEjSZ1rNAmyJX&VIN?NLRB?Wq4WWtgV-QY@P6tI@@D|(5E9i`aJ;9iNC$s zPZru^*>^D2jI#SS&@OAlemkXYoW0&dd(Me9yeWv;7|u0L*`sD@ziQ7~ey45h6fQ}K z*hCPe{Ocka2!iPP#P-?4Q$f%!2 z%?pCfz)6`2v!_sEMvc|=HFeTP{nXmps^c>gGi5b1_vkyVCfGbb2sR--0ji$znNu^> z<+U{vW-2!bX3s94q#9}~Tt(6D$#DCx!XRi**_ugJuJSkFo5R7mV{iyDS13~rpBT$O;+cNsB)sETywz77A2c>VR$1_n{%}gz;nW{m| z)D#xfOf8@BuWiglrt}DcZsh+9#HRIqwzcwy!)3_zaP?;`sB)+FG&Mc~Wk2ezoI5kf z277I12_r#WFa%V?2T_r5!S-gLDV0^uhaRLtdB7tmLRaeV-@z2R0WLe7+{^0ubVpNU z_)ezas_=KBpPZ^@YFTv^ljzxW5a56Hw}5J3 zDTx2o&jfpbV?FK#(pG&}P_zBlQp5K_1=OcOb>uEkLvk4?i=3ME!Yoi0IL71tpuD`l z$DSTLfEucUhMURr;Bvm2%#1mi>L8d{Sy2fD<44#K`~#GQg8i)AwQvo|fuK5^eQdB* zwCDadCOd!{%Wj}5ZV&2$iprS^eZemy&4izNeBI*{pyJrQpcK5;<2XZ>ZsYMGle?iw6b=MT)jKH4nvG^nAvaE$43rpIl@nts)wI#5+sJGEeXX8zo% z!tq-2ipkGD4$tgRaR4TFXxd*_wv{qREGu~V|sLf%aG5&RW8q$D?iry^$@6j%_yId zDVQ`NxD_r3$R20)l-10iIdOV9zr;hD=ymh>CVr}L0^4g&Ggg~*h{=g$mvHCJo ztP)g%FHv3!O=DS;&$pqx@Obj2*x?hc+!)UXgYu9=Dc2J$1h-M3-UeYy5(Y88()c?n zmTSES;!5?GPPU2`gK8iHs)GH&ZNMEsx!^GTMD=U}*T_BLJG5W9UH>4cAaDt|EjUo= zS%hb&Sp};=DKIlLuQuRwUxih8na7hsRWQxtI8Y52d)xt3!<&Lq^q1*ou`j_*;IDX` zTxI-ec(xrGD-qQ2&a-SN?t_Q$s>+($i8VDrK5ir5_?>*GVA;${?Yt(;u9;9dpRzSp z;k3#c&5>Z1AF`>S>KPAeNC+0r2L{4rVfJrXstJN`TG?1mo^7}Ru7+xB3T9SL$^_9| zGj;cQW`P(~Ag>42<1G1d+DcFj9q)0B$Gtu70m^4~0#*MubEB6w>6`s=fmJhMp=HzO zObzby0qJs?>7e@Y5~%7|gR15}kJXu)%8EIe;9*8nBYPL9>aPV=ZoKDJ<&)>DyTjm8 z_eQ%uTYou%2I4%AEZtKoDklWBCs{?OldcN?4c9;{^1R_>Q*1(Y*~AQk@H1TH(x+I% z%Rnh!>&xdlG6Ami8JX%SnLYaiL9XFwvGwrKQ?2JGdwwfiE>w70v}OAp+ZLQ|_1}Mb zbU^zaN9CVk{i!OiE~`b48FlJq5PVF!x>Y+hGgDoE-t#v=b>pWcHZyuLmQv?Ecvmnt z%#SXO9&6us*W1oEr5eDk$-dn4yUwu*n%j&oKPTFy~KOPCb&f zw?zkZ8j)>#hc&knlvB9olY4I>9L@O^skoB{6NKSU8m}p1A>p$X`5K#{Pg!4g0p;sGJ?})mY>=z(q^Hd%a(d-9Y0|H3W=*tH zm(8*}K5IH}0e0ltC$YD5xZ|HT-dBN2U*@r@Y<+K2nRn%mIgUL{XgFyA;!`E$so%)*1kJ>lPp{IgU^x`*7p{1ZIoCj*+ z=CwNgE%EKQ=8RbT21B50gTMnWb~!L zHSd}N6)3`tt^XP<0IvlV@eX_6yk`x3SNQ+dzAnp!J5*Q*`s>E7c|-Ssxyl>sZAfza zw;8i)-4soHY?hb=$`WHecK^gITT@k0Ub|-yR8O8XQLdMpo_NN@ES?c$=5Sxje3>wN z@?_l(e!C$zb_F$)YAS*aa1G5#pIHMFP*|VGNtbWr4hE-YDyj-*WP;a8*U)VLg}G!` zP}fhaoIwbw4Q5o%a5vJ|CW9#I);l}#Ycs$L9#dbL;_rTG_5Tdly!-;(1e`vJt?ASt zcpENXdI?nhRh6|>WfP|dYv5}CAy88yH{b8{JX2OZaWe8dhD(nnO;X6D|BwrOT0@Z<;znYU*z~$`i zezW?nqdr+CcUN{Pysdn2%tq^JDX0r}^SBKtMLKv)c$`?tT^tL=Lce-tIDpjI$c9A1 z-PSz?YIVEW<3v!3ode2J@3cy|WpfTZ+nt15tUZJTnY2$T;f8>9pthDpf}8a@^10VY zSI_tL6@+mVbwe)3b zs;V>KzM)-MHqEsgdJP&G?^+o|X-|Xu00ymbkXU?C<-AsR<-W8PP+JRE+kHB!| zCf2bGD8+sVveu)|C?KbM&*Q31%@P%vIWyJs`{ByJ*5f}wRWK=&sbc>dyy~x83%Yb} zwX4f#W->K$vYKFW`OHZLv#WwJ9j(1#9;>ni6_pd&*;f@*u*BB}!#1-5nVMO%Gu88h zBdJgUueOt!bSkJGb?~1}-so>ZdOAY7xo?O3< z22{@@U{~-)a0_tpR;EA=h_h$wCnLyNZr$1%KGO5!x3RH)98?cSkS;6&HMV8*G6fUy znq58b4630J)KHyIgYuOFds_K_fi5n9=9F4;vO!Sl3rzB3))T%B7j6Nnq6R8*Cf>ms zI1rQxbGN2j!Il4RFV~~`R&e$F^X(1GnWj#W9j)F{P#rjk^v$Az9$RJ4-pSjvY`(mi zwZJ)DaL+C_kz-IAod{~bI}ub1qd;}J5L5))0aQyJL20xvEvN^Dpt_zrblAMWrq6GE zT)WZpJ-Tndyl=vV*YiL*5I5@D#0T#rtd8qg#!ftm1!zQsgsGeLW3SteQQs?~L+^)s^oS8=o<0c69~UuY=1rEQaN^<(ZnTsZaj- z0}ZKT?||YDfKvP#m(G@D9D)X6dOvGuurJsPR1ZQ>Gwt&|Z17$H#UB8bAA_3Fi$GZ@ z<8dUYdi#PZ-^ufzX-Bc|EpY3@5S~DgW;cQIipcZ1Dgb|k5z~ZPJIqXbJE-Mg8MrN2 zTV7Q)p9e$-jxay085TA5?3=w}f14MZbGh0lbh;79j z;TyhgjP>VyP>L)DwJ1z0n_p0snG&oY8#Qg$Bb(dFjqsOE9cTJK2dcW&pd8`wL(HMZ z!`19Qpd9ERkGWG{;__^ks9-1RQ*#_BIgiRcR(y{<_3K5D{3zbOd-nIkZA=Ts+t_ak z_N2_WN7#_P2Fe$nKGJkp>G=(yd}bM_S+Wq6iDrP(G56>)_vA45#IW};oPGY7iG3Z#Jy})zI;vd}_EaPi$bL9|WhIVAuB}UG*JS zX2u)^D#+S~pIP&GiFr@u1k=nOZ3n^Aa9MxIM04g$b+w-Q&C5Ogcz}EjZY~_FVz*pgiQ8DjUd&Wiv7r<#m}S zNmqAr1A8Z2cFPU$D7Xe}#_XC}9=T?;vTAFU+kyF=SGImI1?3S1pxht(y8d5UYp$CG zrARn8;hw`i4EBc4pJ!q0u0@HUKfH>a*KT0vdMkeyxD)(ba69m$hJ-u2c@R{&Q%P^9 zTSgwRPOIU*v77{jzvDsKb&;?5KyWX3cW_VemcKF#?J!f>QlfS!2>~s%}*94 zf<3^(C6@jy=;Ai%{YXCp>?=#~S+lL`%PMnFWK#J-^grEtr*w4nh#_1gZxKk8jnPsqgoAp2ry;M}pew?FjAw z4!qDNZ7a{;_Z{ds$I|bK&DUC8kq8c={F=)X*&vN@+T{s{2Y{vU&A>t6>z5@0CS3gu zpiDmplu3JmyMt@5vHX8uiYehwdAt-XfX@PD$^AU`0A;x!FELzu2~H!^T}Og&5hzO> z;c*X87HQ{k{l&&tdc4?UEhwiP3(6vWJmz~`f00?}F^^Y)+fy!^MUZ!Pz1=E)9Mmj0 zW`4p2^d6wr*Z|a;`@uZJ30x;<4M9!v;hy&c6*nv9+4P)sx8Y5gUP0isd(4t|fa-8| z34(e&3)IT6l7^(&uJ>ER$CIuAJ{nY5+8dOD=RRQRxd44KT#8gppHje9zWD|vcVm*f zQQ^L%<^4%?hJRRF}`FE}I+#6|?QS8Ph3D&I0-5f+hchwMr=N-EF(#Tv`!r%0|hj z4ZnTL#%*Hd%$Xd-2hWkNwjTqf+jpy@y4`ln9{a5I^n7}%}1yn_jYtuk;O_?i={)MED2k^3%DTGVA(3UXd+X z25M(iRjA?nNYD(LMM3o}cV4{dt7e%zP{w}^RQ`~EnQ!a@sz-UR*}AkfT)uM$TpgKM zUQj-Ba%FG}_3OF`PW>Rb{&g&?Wncjf%Jfq}nffqLreE`h@lkL!oU5p8;>1i9Cw+Im zY58CJA-uy6UG9K8_e^cyw@jg8Q1x&WpnY2&?^;7Yy={vA z^^|5k`Ko8;dp6d&(;5_*#DP+Vw=yP{(O~qx4dJPvEL8VC%Yb@bMS^-h1(b$7tJ5)e zzp4ZIZtD)XKjB*O=$t2#n$C;8IZa zHF%r=s>7dWKQ|3e`@%HO9en)&mxj6Ho?JA|MbX??R#8sQ_taNb&v9`1&NxsFzfS$) z?Y^;cKYneq-w{J3ymv@SG?bjoFW~~`Q z+tbXfAjnzrnP08LxBX_9CpX&AUIUk9Cr@w(Ai?LP%MkB+rC(xTt1j{q0GE0 zg|A?0kaVu&UncDVvg;&bimNa<6gSN=5RiHf@r&6L>P3 zu*Z(si9jOU49E(cHgJ1VeWn1UD03qcJp|2JFEA?O_FZF z=YyJa$9Nn9vQ%d4dn0J%+j{(l3pE-41?tvxB`Aa52)ek*#!vmY^$#1VE_%x%4zPsLAn%ggF4&Y2FWSZipvvRBq$~er5H+&(Pa()qC?HP_Hrdt;gXdId zc=R=~dcJ$w`?9b25s%|R_504AHUP^(aXmR=Q#NHRT=lvqOw?OhUFPaPYP#!hM>odjUNRP!kS@{yU z#wX{Q1)lc=H9olq%DEuxJvv+8*%!Eb7xS!ZL3Q*(P!;AnIwezEP{oS|to*K{V|Ft` zW-2Qj&!-+4WYa#D{yX`qr@X4by_B0<&se|ALEL+Y+06Xhi;B4y694+b;>_}inSxq| zi3??!s@bN&9=lu5!UAhB@R(Z7yZ2w`>>o{qY<9_KUAw*e%9Oj86+M-TUMt-B?i12IQ~AsHdUf=b*Zoo$ z)$d!Ce@6AR6~`X%=@WN%9iRO&HGb%hD+fOG(uN5)*4%U2UHj$d=Rfet&o@lF z;mVPB=Z|?Rb@ywBKGh-r)zKqv`23vr``>-lq@7!JoG`0b>5z6Eie4Ol^9v(?xN`m7 zPfhtImFO5fIAibRki4jIPC9u*UepA9lNS}wO@}+D>>+kuH1wXrWLYX|tW1Yj`oyG5 zY)VBRhnwJivHi5gfrsRo* z;j^%TT$hSg%`8my%nyPg(TWX&^ASd?x;W7$QIyD!nkv)D4f#=VT{_&A;HDiwyC_jp z7#9ixO?4rW4bQbyU0{o!Xlr z?;Dj?mV`5q`n&29RfWl`+eJ+a(#ds{T9{65zDZQKFddG<&vZ7>*7g6CO`<09R&5d$ zFG?p**)*yHp4l{NT$B#C!h;4=GNqDXIcyZHm6Qtah4r(BY6_F<+eeMrbaHrys0pa< z5Ea*_lh=2M>VOYBM2$e-j!_dZuVYl)kWN0>F{%SvZ5A~)q*ME_4h@MmE+`2vMJlFR zH)5%EV$s5elGGFeUrDs0p(J@rr>N;9jM*tFJ~^G-u5(m(ayopN$s|n^(W=VAaC4&Q z3C89ZrfOmPM++-Uk`Hel)t!<~ZrUYkJS80-!{RWSQpu=cR$=%pOeZSTFuyQ))D}_Q z;&f^$cU;4w((01%1*H95c4Bs6*o}2}oV%3fk_);!%za;rX_o(jlbaLpHQRC_9 za1m?2hO4#wExBgPsCY>_9LEMhiI@om&e$qyB=J!a|Lj7it)t>I(%~5P6tWE7UR{(A zk&3M#g~{i(j+!Wu$69X2L!#2qU;AAty);VC7{*@U%*# z16;aH{te99rh}=zJ$Z}X`Qt@MavFDa_$_Q7#~My83=iGTd==HG;dGegF#zE+Fkc^& zB%gy7Gwsl05=G4{N-QW0&mm94$iUSWrrw72(}<-u8keP0hq4JDA=Q(Y>=YHBn@+y8Q&e|uI_$`y&!Mgv zHM$6PjAJs&8!&fGYRE1)hU@qmB&Tx3L`?l5=F}RrtGrPz)qrFMbb$o+XZh4_bl=sp zFOnHh?Oq3K)*{ue4|&nTronj#R9ab*YG1%`M;odB07>e#teZL&)?ewV#+(#>g{0WU z1ejNt>eW|QQb%|Kk{rS!+(ocM99uQ3F!ed?aHsyz-7Q8@YF=Tu7^cp+pc+08JKS9} zbXj3I2+tquSYlRTa!J3a?xJ+~9Eny$QLq;Q(HOBMRRQAQs@}L~RD4M~+;lImKs>Z{vM8}v)OblcdGTIR6M65DXI2|}K~Y|R*-93w zLmJ}rQb22j$Q%i@!ES88}20PAb#Sl%f@s7z5;~0jcqS9q0sauhp1BE{zIo0B! zo#^PGsPW2lST{&_UNZlxD+|M)Vb)*y%rx9;QnM=0!L+orawR*J1Off1?-(^KEDZO9 zNip&o3RCC6`bVXgl!UJ$@&Ecw6gI-muz?hCK_ol`c93HYj~68dM|I27;g`(ZDWo|= zg@+R3CN{JCVQg-FUN`K2d^4N-AM91wvCVmV4do9Lo7vgTOs@MWY<#qEc}Z$0iXG~t z3y{V+>0_j^PAc3tC!K~gnzX@rUMNZJfK^6CE3O-y=LFUEP_xu#xEtc-CE+nh7MNtC zOT;wb$@RmdrW?}9?jxcm_6n0mM0K_4}GBAQ-ci`8+!( zg?CDFCSqQszJ%@LO6-214<@M@CgJ7Q^+^jAo)@X2(al;GDG?>j0#pIPR zoKHh{T4CxZ*hpy|4q^NW3-##PP#8AAaDKmOcm~GA!dc|&zr$36u@R%q`Pe(L0G|kB z-q*K{SJlum%v@O4?K>vtm<`0Tt6}MQ;h~Aa`I5M%P1JB%Vc3TtqdvJ9l*+>B|2-w) zDkN()K`%NV9Jog!w(1Q3ld(gbdvMfrM>>3+MEQGL*P+ds$GW_Yb_U@nnBp{BGd%AC z7*n6I{B&GY%;vIyP^s)zx-@ypAyMO9>F^1knC}+4Gw7F!v~jzt=V2N&mUlYZzwm#D;7?&+;Oy5-x_RTJor9B}~=Q2<%tST02&FgpDe8 zr$dWj3J7dK+0Q-=lSMs@nYI8AnEq_otIj92qs;pH3Zp6naLbRV87JB+tv!PCD84 z=&135bXdle$B%s&y``D0y1y{|5hle^qN*?)c8sa&c7UmqU@VBJ`X-WUa25@F6Hs&w zOWx^)VKq!9vu><8Hfm~2CzHoT#Sf)ZgO1b4vP}rjLee!B%3p-Z8v;$rWO#g3{BSxs z=J=@Y;dHo+P&Uxj$C4Xfd_oXRgKZkEVhCCh^a#t0vy6T|46oGiQ1T;~bR?p}`cAN+ z+cZkR>S4^Z`Yqh>{1e7pRV|kkhFuA(gj4zAnML^!*$=0sft!3*zGI)jh*XLlY^g)N0)dfkqkRv2!8;jL83J~z2Ll6>cpA2^F>Wz#og5XfN{8P~ zHpQGzrAmqZ$449Im4ufeG5#n#^qiu62s75JaU2VlnxfW^i}E1^hx+`u;dN#|Oh)eD z^c`PrJ=-*H7)Bn9`AH>1Z!Stqiy9wCLF~*LpsH|%4`Dc{1{seonQrF9xTt%|^r&%l zI`v(JR*V&^OHv2S$VtnQ4v#jj9-QX{WsaVyJWZbNopi(Gyn@F_Iu-{8=m=iUT_}JVaAetz!EG|s$IWMYPn-1@q*Sr&Ar1IwH9AA@SG;Ayv z*y?ZvOp(ZKB2sUOMWvSx&a1=cqm6S*Qb*>b@FFDXx=RrL*vpgIXEB58xZ^d0n^OH)9)=xWTWDj)8U)hAYeNA zfHSndxjSq{7uH8j6nU&UQ47KEFpUrnulUhmW@HdgEKDteaSMA-N$N2qOh`-&PO`o+ zDJh?Zxu_Md`W^Rx$$IWiHEi_EIYg?}$vBc$yKp#?N@2M(3&S&D#qJvRMB!>!e;7+3 zCj-e-+^NZ^Q>@n(hA)Qc0$T!KhS`A02RkgzRi>3|q-Rt%yD)VPtbep(W=Z%jq%<{H zyy*xT!*bd_d`r=^o`L*btcA*NiyL)-argyOO1+MRjka!<8h;7S8Fy zA7KhZI3ydClGBZ`alj*LU>ZgkYCZ*11h*0Bw4~*A2Q)KH|C^RXb#JA^tKa%dt%<4ckkXpe;ovi^gl)~MVWe}fb_dcvG@PeckopdGK(z3z!TC$g zh%Pyt13Q3195m3?bxWh-chh07vvU1k#Sy}^v!cd#)5%-UikirKn>lhyR(fE zadDSHu>D<&OzUvPpNzF6bxshlXTe~a&qu*jvb7en)M^+(a(+qJahd%INq*GuMqyYA zQz^Hv4Hv^?R`wsX{4}f|%q{B4UzSBpA8>+nZdCkXI$U)wFAS5{!G)LK&y5;MEIKde zy{jHC3~OO>61O1?pNH*fm9cZ%Hj*!I#FxvER03@3d~CCCiiNXIhO|FedX!!A+^;7wCav^4!R&}+K^73c0pA9aXR&k(zPqvE9&=L zm=9B3EW5ON#f8l?Qk!s?Ov3)-T;f?A)qRpq9Ui-xP+O8bBaWI#_=SX#ii_bs7ujg1 z;)Z9q8m7r*bNwlpd9=JXxY&9}RAb)u_sn)7Q(-baoBA^gQ+LDmk5`nkIIyDM5BHBm3Ov?Qgbg z*sL=t$HIoWAwM5U(~WS=edBYU;WX5ce~aY>@hY_nQ|*M#M~d=9+->qopNtvlO5Ux_ zs?zeP!8;O%$$yK!8`F7j1b#mL?(XbINFY#zmK4dS5Wvzn^fZ=Z67lz&Mu(i># zofCHk!HLQX+#iSVuYF#n)NZ6d{y@j#8Fw$eKRThdTeyJ2BbzH)11o7}!Cfu#k|Xbm zivLKbE+jD>mnIT}^X`#vEc~M+l|eenNh^^!z9Fsay&T*-X)e-nPFja_jFa}bk8V3D zMzU+aMdHARYlq*TD|a;#rx`buBohy~$3R~^U}o4X(tYEB59aKwxpV={?4>Qjn=tu$ z7w0frH)^~Wo>&qdiln&0l7)+$3bUx7-Om#$MV^}<;qNd%Q+ZG@zlxflZ)WoNHji4Y zauX{ofyp}PaBERMM8VEwC9iojZVVIY@IzAlJepe=?y}0JdEgeFa@a6aO!r5t;>NZK z9&J4qH-X`Z$8z<_^b24^C~Fh%VVDMw*}=rCdfb#^bAL)vK15N`c4Wb7Yl4F!3J!xw z8TXJWTmVy*tb*9{VOS}Q)c{-n0@H}mv)?#|T4Rim#hLz@Fyi>S!TAX4v6;Ew6S-iI zzNyJD&f{xK!sSRZlAFolD@u12v*;u{J{i|_ps!<|w8q=Hr8^JS*X?#2ktSPd8b07D z1~OW>xFkFUX*}r`?cP$lE2SaY;^`cdn~a62b-KmA>Oz=Y&(7{Qz=pwWsoC`zb106I zxo#>JMKY*IZtd|B_8F&R+-v(_wy>v#2n<5~j=OE0?YF zc{YaO%Qj9-btY*EOrdqNXy_S*;S$*XFoF{|RxiFBH*JZvyT1|_Zv`IlN?Zq~?tI00 z$0;SLo!8+tap~5HlGNFVpFc6}9#xXk1wuf|PEdWWQa;*FFKclnp8 zK}tKKrtNX3b#7&@4lTq}CVx7~U{ zQ%jMKavjV^lF!)0@AHm@RU5>4Fiwtd#tiSob=y<>_oVFUO0!s}dcPY4i%j8Fh$y^0 z3ctsu)DmYSE->+1MEZ6;`R{Y2VTmgd=QuGr;Dfla7e-z10jD7Gie3!D%YTZye#niI zrDp#WUqL*?Qg`~OS*$}m*ixTG9An~^>seWxn9Qt?i+7TxNYeZ#4)MI;6PRWTF_t$I z`fOnBx8mn*h>Lfoc>9lS*TSLMm4(T9AIFVKeVWvJP+g19_KRQw-%*r zmoXBipkZfK3t=)hGZM#t9A;-ZiSMi@*5^W)EZ~AkYCg7RmQv`@!slb>|;!C&?hQH8>V_m|gb*n;h8DF#L`o-}J}0 zu0K{B`=cpld+hUI3e|4^oqXv>J={p`{O=&xE8aMZdrTx{+kNT#FjI@gBb@wGGvoN- zW|;bg%G_st1e3)~t!_V?Wf*a`6{CNSiwg-W@y~G`7`{iYYGn*~`J&e^IzU>nq$GLg zFL7}Z9=}JEUdLE?Zb_?oO+oxnQKB^U5J`nm>8z4g-;q=@C{fyK$6tf^M1)lRubex@ z3+JOek_L*k_0hss-G8HGaiTOl;x`NBY>Q4TY;_*V4=MRsk_S>c8L#S`C`^@aR9x8D zg%JD*;&9g)6bxJaZY&Y6YUF?zR;qjIaIqxUjfR)=Fb@}eLvF2w@vl8K<2YPC3FwUX1W?leWp6r#v5V%+$9($ z{1BFQdD`u5$Ln+EC-_oW4;yXQF@>M=c@}7PYGWl`!>K8-ed5x5PFs=8@tJC26HF~r ziiy`RpFe_fp=N4++_(?UaaDfY1cq;tBg=6ng@zqMW1LCN-~k{^HM+dyRbkw;FIQ|N z^%zp|XrfYEvw{n~;Y~0i&Vy+#+qUu^n3S`~`V~yZbW<_xv#FH~;#KDm^EQp^_CxU(N!1v+ zsTg)|Z-Zf)kA$i2;DQG3ggvoc%PN=}O2iHQ3Ch1hWmuc^dvu74N8%xqJ5WgdNnM4+ zoijn^H6-uw!|}6@#w>mx1(Uw!Jr{ZA_GRI#Fl{%iWc$tRItwRbVAf9!>siW+HaQ*kGJ!kvMcIlngtufHJF1NuSa2fx*8g|Mf(mm(y5?x z%aYA~N3@Ge&e}X~I*2YmN-FoQYfE-6?2-s-RXX_3PY=?&lm7V9Eq7nA*}m1fqfV~x z5;u}2qb#WZSE2zwM-by7Ue_aWoEk6_6c{aWlo-& z0v+HUtX;N!+;jxZT(d(?72Tiw0@KRJH09CQp}pe9@sv5Ymqjh=C%%0P)6(x2rBrF} zgdRikj@kv1lPslk79TH3U9lrNI4Rf(MJ0v9kPe`5t2mLxSTM~fHbk6x|JKYJ zF#Mi7n*-y{9L!CEv1{Pnw&gp=O-D1Z@9!Mf9fM(B+9eSjA1^!xzuRNiL~yK$XCoeM z;-`p*m^gSh;=YMzAs%Gn8;A#**t-w&#EIcdM2!y7?##kuW1qO`I83rbK_Vz5H9sC& z%zq@bTDEzr^+ysWj zyXWG9*8Hm7!1fZx@sRTYJ#d~$v9EV7!0rx!NG~+^)O!B!p*)z!qtg;Lk2bvJd4QEKN&In zLzthi>_xUK#HrjCdoGf53O%900F7Do*TA`!G#6+Gh)P56n+DMm=wk zTwR-mfk+3tQmZ~J46lXB?Csr-xJ_}wJ=M4PdH_rl&~}v_{;i#h;)~WuyJl_NUT5cOg_=_w+gF5r5L&d*kze(vzSqa$kqGxOUee z3HLsmuj(wQ+{DxO{*&%Cw55`%rO+edjnmmC{%6MEeb|1*{1U<7e54lL`V4x|pVb%c zYca;Rb{@1v?z_+y*A@*+xZ?v~I59jIt+a7C0;WFO?a@83#vkCdu9GY9#Fmn9lc7&q2m;NpufUjB3`uK^i*#Y@G1iJbofrYz+wG56BTc3hw|az%UB_CVl6F=K zvsjsUwkRLs8)rlO4NOslC6`&>_h9ne1CmKdx=FPK=scMApKN!CqfZ(qV$kk_DN5Kg$&X+qFuaO`gmBx#atbQU9RgD(w`tsnoDI|64ngYs!ql^{wEj9E z86F-NFT&FE4@b|qbP>zgy@#ZMaV8=DH9gfV+4h5_EFzcgs zO>e_wbxa7`;iz1mO4h)1S7LRpg4z16yiJeJ?JdL(hRHszWa=CkuNytjtD{H9O$}(@ z?--2lcBpfatRyx_-7Dt0lKlRdxalOu>DgmVS-XYW?l{|+;z(F`8cZgm8D7)84mQ`F zXKsDGO&ro0`Lm9X>rSDj=SkJb;M^Fv{R!sW)U>uRx!(!a+IghfJ&U$5PrzLLaYH_D zzm#d+-*^%;^LRvsdu}j#aW8u*)LvO$hh#=k8{17VgSCoRY2}2etM;Pe%dkCQe$CCB zm|H;9@UgH7WZFzv3Da_7%f?SIRm03;X&5lcy2_0NY#vP0go(kP{vR;SX0F3~-+`IO zgj;58<>8{!3R}(3B%+5ZOH=og>iuXVVv!q%aKvP@T08fY;BJ^2K(VKJ8yQBwakoBG zkW4AU$YhwD$dw9jg$>K85q<-+nJh=yajLE6l&s@jIO``ffZ@Ce=3XZV-$b$=YtXxw z=a`PSj)2)-B~i+h`UdLVcVkihw4A|Z)3ac*qPsgttr3e#xdClA-7JqWJ}b(HNLSlM zo+;*n=JQByC*ZcWZ7Oo5)Q1CM&V}QI0yE6QRs#d_Elg7;h=(rWFk^=7#WW7*BiZgj zuff~`#5IAZBCtaS|X^k z0-qz!j5l7xsr>2e!7cG_#8XT>c8T)!93;6U5tnSuaaj6{xcCZ;wek$qw^F5NHdlB* z;yg>;eQ9&!*CF~REA7rw>O!^Xq>Z ztT}NNA`i8BtkLD%=ET$f6n{aivZ~6@%ZbU?&x;!`PwdUzXD}mP@i8?bRami}=QoQ> zow#Ca^8ZYnbOGaPMV~{Q>cr%M7sgH3Vwx*1OaxQn(kt*7p2Fl@Y7!zxp4U+k5-(Pu z@7OC6!NXS1CRZkcE8>lJk?kb+9BTio62WEm7zpWnD>?7#L~x#oZI^3QS6rJIyc5#Z zct#sePr9@?L0Lxn)};5i^z}LKbr^QNj!#ktU6bI?q+JWCYh7hm(=3u7&@R_WT-b|5 zB>x1b!}ZPMxmcp>z^_Ptz$$NO7C%I+cKL_h*gPOlBHAb<3vY^xZ^2ME+$8sMdUUy2 z;)?HR(n&7yTZyio<8R4HYmxjY4Z5{?6s~dN3c1j2&53gnZD>;OI&sD21MxT$i|)*c;f08ru|b@e%d-9p zba=e+0v7!AUClWxGdte{JzfR*B>1+Lz~^exJClCmwR?{$jwUT~CtiK;j*IUkRxL6N zU%opRGlsrUm}+~ko87n2$TGxgu5J^r8pCs@iY=z&XlW>{A3(?zxuL559N?X zxURJN`u;@pYkg^I&;!IZmz2Ebfw=e{qUkpzxgCq!Y4>@sc>yDw&4;fZW{4fRAlf+|AEkMgxanI<)@K;3L z__LH@uM-~29jLJD32%nUmI=4)Cf|B2Zd^e>+dpo<|7NREG0dxfAFqGpOt6ZZp*vwi zv}H+cxtcw;>wL0obzHm>b?zf+k7&iSJbznbD<6k*4B$wZb};P1*ixJZvwZ;D#&BhG zp7sb`pU7RuAf}FhxyK6OIY`5#w(HW%Fb%b7+v-WbKhiB`YADS1j3*-5P7pJs?u6|b zm+pkAK1A&2-UaUblvPBv>@4<$nPP0~!xLfhAZz4an8wh$u*uW85#^{Rx&PB~@oJ2? zlvFjy?wH3DufyE7&G}>3XL22LyGocDME-pN%x{diZY@m4XCcQRiDykiHua3&9tZ6Vis=`>wTX|RH2?incPdU$uCveLB5p}nKBd`lRA6DuzQePnsw=#RJ%|$b9 z;^)Kmwv1;HwYB8Nh>Y&faZ2tok|D2)JVkd0K5vTJIoC9pqM}X9J77GOM~PIo7j#hL z#7e}GR{KW8z7%)!Gqu}`{M3&!2InF1Fkaf$ zz3yWm=wfM*e;j8k#la9oDY)^;rQHnJ^yFk z3q|>F+6=KtJ{)E-TYh_@XXHIy7(N8^%@GfOg30`5fw6D3WD!g{^AL{M^&(8x=@hRT zK%d{vl~hzc2&VbT=9)Ql8cgoN+^a5Z_2Ap?mNRVijtflS2#1tB6cpiMy@W^9d&| zK(t$(@L5D1)7tQj+`w#UW_QCh``WreY4@@1L~Nlw45p#Yi&u@KS1{dJTl>GlhP%HZ z81hNZmU`D@5lpvkW|!w+e@>n7(-ti!M}HbOzRMK2f>c@39xDIDyEN|+9R_?B*MVXE zXEqvm`XZj9@Lmk5t$zK?xl3};=P^H|lMWYro|{|>V2{DfsWml%uWWQIuLvgpVB~n-Q4QmX1^)3il7$u>G4KAh_e1y6 zVWns8d2aIduj8hV(0coCEL`w3`*a?oeiPTNC-o6h&CYssJLFsTz3#!=tncIE4J;RL ze(yH?8yN1Rf8c=2Zcbi8taM^H^hfsBDA*=W=n3NO&{LXeYOjB@akYXMB2M@-=VvIJ zdMZv%{VA^dgtpfGBrnt5PuS;YoKl6O2KGxaOlQ>=1;2vnEWjLW)GxMw!CcJq3t@VD z#!9{oYj(a?{hJc;iJv7(cRsJlLM1z9{k~W8XFloOx%lX#_x|?thi-V&y@LHQi5ejok`7^We^QOJ83rZC_l zQfiBhII;}iYVt;$t)?_wPKp9DlY>8^$@`r?#~TmAG{X^xkipW(60;a4^%zzB;~|)P zr?}O|-_htRG&<}L3ncyBv(oE%CFN^|Jui`TVcVRu5;mSnm`3#RHP|rMfL`L>I+=71 zs26&9TP5k%+|c>$4KT$a+lqV&lU~VqRa;_VD=SIY@$fR3yo?t+*qD3=JG}Y2{qvG; z$ojjD&^a*GLJQ1==V4OCoGY~hzd*;^CVFo&gQ$Rx%4~dMn98v%@Hpa4nB6FJZk=>9 ziv#}3!d3^jPDUrrE)6466(d=E7>MU#+DF)SxC_5gr-hTX{{-B5oTu&G3ULl>V6?El zBwT}J(gO2TzYKxL@qB*^drc@)R_D0+vI>SuKPD*QyrRF z@ga7PD|zolslSt|E^;S8tz-EOJTW^gIv*xq;m=4|lvcwu2inWelI_~X#Xl3`rnHOe zz|?*G*j;(N@Mk9UwworIoK8IHPjNkBnWc_spVY%@mA&pyF|R{1;JLL@%Mf{PE%9N* zBTekW@8KQc#IPPwq0OG!JOWdICj8^N-T0ZjLtz%YD_{zA+%{vU>tSYJZ9qPTX-B|c zPIIueE5Dkjhw}8DC7>22*WxELxc`{SPwVlrC?1mf9MRuyAGo0UJHEt3cQH&QH*-dP2d1IoC0z3QY>^ubZ8gq=X&zWdK7lo_Ny(zF zanm0R-~6uDJ<4$X-7s?zb#Fb)cCzyMZTPi5F`EI0!OVS>cY)7i%ZJ;&-khh+P$9qS zXQrlPYBr4L;k-rtI1+a}JQ?4JB=c_?HSkD_A6(OdgcZ4Uy_xq;S|#~wNq)KS1X7s{ zjL-s@Jx@I@5`zX!9OP7v(T8*Gz`lsLE_t!Q2w>^4@`I zkMA@HyYL%%`lb0G!)#mujS7W zVY<#{;JYw|6x)|>wSzIv1y>g3dyP5 zSkj!Qg<}=WRD`9zhw+D~>WA%Q-C>V^AuqwS=s>vIGe(=4xy{bG@zoqY8fMc%o^Ux# zRbmrfA9|bDV%jIuscySahQHbvoZN+<#beofh6Jq*Jh6C`Cl+TLY6r*)A}#y9IbQ3pj8Z=B|qy)?%Xx@W-n|MP-VF1_ zSaLUbW-4vZ&oycV;x|#qn*g)eB17E_lYX|%|EQVCY(4mSM*g%1!K(PeUX;&|I9kUQOa{YrJ7}xKLoh8rj1!NHQ-yR@>ZJ}s;gELV8g6Du;%KLzZS<$J(I=Rae?~< z3H~+Q6y(%7Nb#M-S07=L?_?1^!d86C`RZhvvrG38s$2zMrE^^FK0CT|g6mCJgpmnf}I;;TR}e z`Uu6(@OY-jr5?}nc(%uLKz)QE-;4OlikIpyLRHaaeDx8k;j8$n;pKdle~mD{{K#b2 zY#YQ|`Ks6*eD!IGs`qZb8pQ|r>hcHq>Lb*3EBLDVm3;LPs{CpZK0?*=Bwv+#ny>14 z#%$*V{Qz$8FTVP;L=}9^r5dWDH$4}s!ngU#5F7X^|5J}&fV%!mzWNB2|BVQrZ!~r) z@GW2AcYLM!PoDn_>eCWc?iao)_d8#?LLP}qPw|f$$oJUJV|!4R>I5>6f+3);AD%_v z|G@~4`-$)oO5ERbp(+>!>cWFTWsTz>S>RBQhkG0k^8esS{o_%J9uL=$PX=Z2Yz=}c zo(rm?`Jg`k4OOJhU$+Pp&vm5U^9GM6fvW#h74j(YH2y)6Y;ZP$dK7^&-G!hoxWx0z zJ-^!Hbslf>cpIpXP~ty47fQUHf7G!%LDhc`sQMpp>5PA)&sgE{QIC&%e8M9K@U8+5 zyMufxcm-6&uY#)Z4bM4bbmiXl$XS{*IVVXDIYDtBVQbnCIN5NYmZ&6-H{7Qssv*t{ z^gH6gW@`Qa5H)N;Cn^$l_PDvnE*`l?mKbB}$QlNEeRw`Tq@7Zj3KK)|VHm z!Ev4o#Sisd$aUG^FcX5qJs#l;{2j`dkMa3JHGC|nijVhL=JSQhpWwMre4^*VeBIGc z_X$E3sPJ4UJ_A$(m0+9r%K|2iO3e4!bskStBA@?;Dzb=wR9(FIGf6S)~Ro`mQg%Y1skkF3(1z+GLP#3=J@fDTg zBUJi2Pzt{3(_5nQ-y&TNz2ozR;_rDbRJr#-8Gd~(NUZk-KL+`K@EQN8;O8QLK`H!$ zPyajAbwB!iq4>W&7peol`SgwPpef1DBc-EL=>G+x?PV<1hLp z^RsHE9c5H@6JJ)S^iH1t9V)*Y`6{xt$DW|-*$$MhJ9ys9V{cHO7O2(K5)$!kdnC7w zAKoLGAL`iS`z(ue2afl_shFYx~+N}+?Pm#2flG*BCgS-zb=(JxbfCYXk`zFC0**%bC}HuLYZi}FCdg+*Mh3xI-lMWm43Z1ccaf2>bjdj<=^7d{|+hFI=D?2 z#0T|HcHKq6@g86P?@;yKOMWW8ynk}1?O?$}Pz}r@{<4E7B5WuD5-%rubfKB&j)zqmlXA1hYAi%`He4v`~07v6d2_j813t8 zi5B->Mp#BR$YH&HI{A+#wb)Y`NL|ib)d*%&3`Tq-)S~vRog;MJl z&p|u4xYZ~89ZLUykgql70Z>I(czg_01CN8M_z7itRQi)X{TW~GSx|<5-s8&}X?I(?90sH~%4R+T| z`3p*ct$eyr3ij~)?@$VC>&tEL%L$d9-N7dawJ+=Axlj%D^<1b6cK2K;-p_NP_#U3O zL@C(c=MMnY^FdZFn-l&6RZ+38=)a*99PG;pbzQ0FEm2AP`gEbL8xHcHH|5CKXZ)|A z8raX57pfxtm$mc}D*phF2YMXk^M%Sk2-Fad7nlDX=`)T3^%1Jz(H@Th)zEQ1za`4F zWxm`5P;N8@)B-RQ)OA&$K0@(o&xLN2JI^Nw)$n}JTcR#J(dRGl`9cNIrJxi!2h`X` zpz1jv)JLfFi#%TD^Dl4hBRzuq{2l7Tt9`yuOT@jP(jWBcEl~|Ok}ic-fZ~sUD!N+# zc;pW@f`94USUr243>|>32X)~mpw`ZxK$-M6j|uFG7_fOxe zck=oF1M0bv0>+lU!2bzKk*%mlRc+5dQn0tL_wP{Uvpf2NyZC~Chbp)$`RY+0P`m)t z@z*|JC-6{E4Ibh1$9p^y)JLfNV?bH7%+j+?P{L#q)WB5BaHtEW!_{B~DAQJXto7xD zDmT}2p?Dpr`WE=~mZ%{-$)z*?Cj2L=!IS-kLRGXFlmbgYHLw&^xwAZ;?eQFs%RHXz z@jQ?E@9XKh^M#uK7x;t=K{a%d$4h+rr5-Qy=~sBX%BNrL`8A$j>+w2|*L%DHREWRB z^E<)q;DH47=pLVOFQ|{ORXk%ZN5{%u>63-(z@y3rANT1(JqvrmbD>u4H#`^Wx;H_M z_)gKz&-G%CB$Zw+S13f&Ud$(J%Q&3Vs8sqHjHZ=ka?`AE6BMo99B6-{|r8 zHVl)(LtCb*%50`7>rn+ec`lTTYym30t50uvK@Zp}12i4$_9*+Wb-EpAGO$2p)cCs%p z#b;E2Qe?JIp9AXC5>@eBUw(nl|2tGe4L-jmYLQ%O>Dip{A1KYv@eQ2sD-fy&7kMs} zA{Tr9-%#Z)p`1*A6)4Lt2i*WR-UF(FdqMfg!=A4I^%1J4kAW(` z+UKwF`7Kf9p7G`W3FawGyx(T6!guc`ON|X(5@+7Q)oDBn)FAEXG0@riJvq-Pg0%yZ7hw{=UBN z@Bce5?jFZ+J)g&M9>33H&vR$fntw>u3x4EWRsM+^Ke?)1t!ux}J+G?vpSy8Yy>J~> z3;4AgZ=mWn)#nfcI-d_ywQqcN0Q|pH&DiUn|4&s5*2cB_4>hZ0pSE)iRW)IIH?FFE z7EIN?51~d-hf;NxjB-AX8p-(WR4w2V=h;+EznrRvsWBk zaaB#Y%GK3WEzlY&8_j2(^YyMiM%DC>Q`K&RtHrK9OVxsI@n^u-4vp~b*3yGt?w)^D z%=53g@i*Q0PO7Hc<;Hirx`(RQdC!ekQ`PPxH~z84ZSNBLaWvbn93OD?8$FGGN7Zb8 zklU(}u7l=xMzuCgV(MWx-c02`9}fr52xx8&7iM*HAV8 zYn@;3{DyY?TApS!!$FFxvs}ZQT%GHDo~!B3Z>4%||DC?QTDns2a!=pw>OHRJx_U2F zFSp8#uXf`PxSH>3fqVX8S0AD3FnfZkhpM*22CA+Y&$Q!xPBVTEP%UxwMXFZ3l&TH2 z&DHJBU!&@ws@vlasG7cps`mTb_~%qDzyVhqs9J#UscQFAJC1Wr*ytJ@b`6eD^-$IG ze^a$UM_v7gsu{Ih7F^Zy9xfBAzPenvs&@Wv{Qs(1JfRn8>n03v4NtC`VW4wWjklv} z4LZ8Kic(~ozJM| zALW{zT(x}Bxawf4Zk0y3cB-0gq#IY&C3mbF|6kPOm#F_mLuUKmXRJLk-o22j7Gwfd z3p&ZUs{u!jy~nCp{ibB8&zxY230e9(><@M@tstyag}pb`P)=YzsEiQ zo*RFks)wrHWU@YYPwaQ~3)kS}suwump8wiCuc`$<=<3hTRrSKZxZ3FIukQJiEA7tk z`OP)>-8DG5@>`0_+%fuxdsg5NyaaApDOb6GC!9dG~8$#6v8RiQUIs#9k) z?X*G@T)oOo_g~eWdsp*>W<1%|B-cgQSP ze2J@Bu4bPYSmq$d)#a|PaCN1tt6W{}YM!fWsM>NyRrkwN&WovfsA}*<{-*^kbv5fH zH&CW$d7NBzF{p5Ua@88{aMSH{&!1d%vDoAM%&JsUEBVTs`c;=4ss%aKJ+G?qP&ZuPmE} zYxn=Bww(X}n+Y`UG42IUt~xue#I-;Pu1=(Cfu>TmtCFeO0yC(3sOs378{}@=ZgCCf zQ#HYDR2@W1T*Kv5y+AHikCUr>h4YiE=CjJVs@mO0)q>pb{QjVp^*;`3kjE3+w-34o zsv{ZyjH(IuQ?+28fRr`T%{J&M}xzfF7U+9`$ zxd7NDJf)kzp zPgOIVK)==Zd}!! zPA&IKE#C*ygt~ds6Vr%ha3j$}RWCeKBRo{K1=5IK_$H!X2*wK2YoTfm**06wFn&_4`1& zF7U;mmhvB~nzVi&sP+3m?oes{KG5+~=H%}cY16cRAL#6s-$Gzh=nF-<{rFGc1JXv+ z^R3?p(w1obJ`jh3erRg_K9CNF*6#!9rxic_l-T}QHCnzOq|0sV_kmi!4|M#!qxJhhCw~t} z=ZMDj2HX05p#SzgAZ?n#d?2VBj{o#MAhpx;y6Rp+)k9U&4RzxuS5fQtfm**0)cSp( zlfM_Fv!(U>K&{^gYW+SCzuBPQ^lSY-Q0w=BTE7p}`hB3*?*p}dABeYu*6#zgejljy z`#_usJX*gG)cSoO{n{)K&;L->m(}`xAU2FTzFWT!)cSp(*6#zgejljy`#`PV2WtI3 zkZu|N`}cy>@7(%*p#Sgh1O3y9KQrlL5%>9Sw?hy6j`8@};B~%}EqAqVkgZ<_cdaKt2%iI1pmVj|1g`N2DR=V>76 zF(Bn>V4#%<0v-oKo&lmQ`5B;GP$?K>!OsGzg+Ru$z+kHoggya;Zw7`~`evX?P$L*> zVb1}XPXam50mH0X5U~M>-U7s0_7C%1YGK=e%;$id zZNN0E7DQ|TqF)A*E&FAlR!}dPVNtIDxz7XnuK+1lCx|Hl;$8)2S>CHagP=)}X0h9W zf){|I?Z9kn6vS@@5?=%6TH$Lzv%vd0FwYWR2Z~<=N(JfWc>_o)1ybGs7FdZO;3XiW z0?4rB3ZPt2DOhO1Zvv@hK*pQEBC8ODmIL8CfW?-+1E>n20R`KEqAFme zH45Ti0}^)wtE_N0&@Ax24Xn0=w}Il?%ibO#XrF0jth-vz1!HG=gP_8ySA6UccFc+9E=5tTsn`#_;( zzYo+3>IF|)R5g(M7LZ>J6j_}hW)~3m0kF~XJ^&g7O@d7p`yo(J1r&VykhBL#`54%0C4zu=fRImsQcM2Cce3Xt+on=x z!L^ifOHe`)Ap(Cu&B=| zJ8gwZrPZmtWrO!qc3B?98a`lgn)b6eyDjz$px{HG=nG(vH45TC0%{D{YheXIvmmDc zc+aW@#Wg_Umq4`@ehDOf40yi+KD30dfPhbcQbCP*>Ui~!ZHme#R-#gCfd?r2ELr7K z+otlF1%FNX+|pF`TZPIO*8LmGmzJ*bmF-rkv#@%~0b8WQ|sakpC+XXmx^ugFxJG zKzqyk4T%5Vw}UP2;EnCTt&7L!_$^G3#s1E7&EM0w=yw`-vPMDi4?toQ5Nw4_K+=zZ z_Ya_pCHw&d90E!OA?7&@lnYW01Kq4dkopr4(hPLB zWc~t#{|WT6^gn@!MxaIzW?_E;wSt_#fIe0&$o&Cl?7xAS-++2SUyC{lH2lVj zKX}x4EVmeSf`Z>^5%&)*`di*VKztL>B#5-wV?eW@=om1-8U@9F_y*-C`f-PmU+Bly zILz~2590$Z!2<*|1Eqp!^Y{Ygf)rn1kd+8hj{qTlz+g-E1491w!wjvF_uTkkN0Ek1DWeMi*3hvGvh_=7@uH`j9ak>NNf)zSYdl0$rtc;047;N z2Oz)?C>11{CkQAPqyzzztwfOO4}^3CrdV=EAk+&~3a+)_PC%6)qZ2UIDge***8LR9 zG)q^x!FH=8TUanA zh|a*09zeG37SswNPY0ISqSJxgE42IzjxYKwK|imF4vUngvaQ)fU?uDDDOn^#<~+QIHf0B!&TNtS}4+=ni<#1oAE6 zOrTs)Dlqf(0a8x`Qu+XEtwa#o0|+?_SZB#+0ab!Z!Fmf02Qp6wGQxq!tU?gc69_*W zD75smfm%V0;7JSX3*?>w*N6Y_h0+KtXRHzaQ|7)d}Lm zfVgvj&6al#&@5;YY_Zt>K=GMCQGcMs8U;yxfW&iwtyXw05O5aYjRZ<9ArdGTlnTns za~_Zy4y2q1Y_k$U=-EKX0N@o%9spDcDh1mu_#|v19C0^Dy>=&a}E$a5ZGne1AzuXy_b#5{w6)boLq z!N5K%5rjqoAr}LmS@Oj|m7r3v--3q#nHK;VLx3->LJ%ry{%Ixa%$)#e9}T`&xZAB5x& zLk@ZDfTURxcNy|CSA@%u;uy_G(&(|vW09o6NKq{Eo5y~Y1YC?HUXC<*?1{^fa*201 za@b=Nha;&&kW$GJkNIAKgkFNAT!H-Mu@@v&l8`v$sK=(qA(=ywO35*gbsm94T#96j zVAVZVF+v+h5Iz#{v-FYLIKzM%f!D&~wQ(*3a^iuuRxM}{M2`XjEqfGD5DU}`+FR6U zApUY7e>4zeb%JI=+!&yf<&6P~hXYN5V2d3KBwYa%jRm?`qaYv-NE`=*Sm8LJT;LrK zbhCuK*|K5hm{Db1R+-fJuUf4AX9I9m4aRtoB%|O0x}YSFsl&M z3c@D>eJp(RUW3;J7BA`m|gXt|Y$ zv^qhvAns~f46wYbf#UH%lOW1sC-Y8rfvr^;XpJft+NdN-v=ypcWJgp6S;7=bjBQdG zY@TZ<7uyt-Ayz`Ms)@`t1_ zd?U~#NVnLzKvD`&G#6N4je>xgK;q3nh85lnlncD`fQ6PY4@jK_lnNG^=N2F|6-c=S zSZpPNDnUp(u*8znfy^|ZQjlfA^MQz)fQp;TWtP4G$ej(;2y!g!Rv=~$ zkaH`r+^Pi)g6Irjg=J>|1#^LV!Ags|4T!%P$iEF(Wp#pPLEJ)MwdE}Yisu1Mf;@}O z1d?t6iZX#U)+h)_2ND+n`Bu0HC>MBd2h0*~2U6z)rGmBQSqy|O08$nM>#RgjB?!3# zSZ~R90GYP}m4e4CcnJ`Z0c0!z3avs=D+s?6c+%4E1afZ!Y6L|VmIcHt1ah)~jaDsa z5JWEpHd*#kpdb^d7d&H8*+BdvAU_+}Y;}TWLEK%y7R$Q}D83zN5|mi%G9YO&P_zu# zYK?+`JAlNyfl@2H8z>iebAU2S$N^H90HuO$=D7z5y%R{e2YAIw1XY5N<-m4JUJhht z0hNN+EjSm5SPEq10u@#vs1<~-0CrgV3LrNds1a0J*u9+3Z`mT1T~@79W&KuCc3ZZ} z+qO?-k4629@{X-g*=uzw@7my1l=m!;V#PVkwrLf!t+v?vfTVkXqWgdktx*uL97tRZ z)L7wapj_a+ANa%)?gvtHfl|Rf^W*`cD}a=`R0%>J0QOt*1HAedwoTN4#?nCk) z<__ZrU;9)NzZw~8v^nH!_ZreHIVAbn*DfwVitk6(7a)zk_Pr!24;i->`OViJUW)`g zfE<-H`P%4*k#foAhmpg+_NOFu4Kj5da>Uo3UWbG}hy*=?{N-!cJc3k7wo8usTD$c~ zWJQM_JKY!Crx%b$<-VHDJkOfY){lVhVuB$APxC=y7cu z!G1xY^(zDl)&i>vf%djf5dSbR^a&uyRy+YT3l0f7+2ALE;&s6KCxKu)C`ft)7`FlF zVrw@50qcRIf)E>31e6Ol7XjVuh#>V*VCqvqciZ$75c(Jpv=QiGQ#Jxsg6)Ey7FY~q zJ`T(+271{xK|~?Ya}y9|X`6sr!CpZh>;5#5`vkD$X&~Ho3u2xGBA)^J+M;KG2El$o zKkN4_P_O}5^(@fe_6g#PfT5d#NL#TPXcin246wn^0mV-N>z@On?4TfNBQS0YFwoX+ z0RoDFqk?D~^*m56*!(;&$c_k7Hvv;ifWfw@1PFZ^2zmh+VpCoKss!5wLoIMCkogQS zcPlW=wh1Dh1$w>+#9G>mK&@b}V7PTJ1#&k7OG<$_+bxKB4v2gS7-@@M0vZJS1*5EA z8BnkVSXBm$v3-L0=YgT+z&Kk`4m1l62`1R!Z9s7euznkmUjd}$r7i@k7m~2M`sV@RkUj?SvrdNT`QXptMaIH<*4pa%Y3#MA&Ye42pz}(k> zX|_!eQ3mvU9Z0sc*MVBWUcn6O{sxd+4lH>CNU`05m~B901u)ANRR9fw{em>>_a;#A zGO+4RV7BcO#J>Uz-2u$C6+3`t!6Css8@v-Jeic~16G*p%f~4)hxJqDwt*rzCUIUH_ zGHldaK)GP^Tfjm)B1nB5n7RvCWSe#Yp>F^|Rls7KQUz2AwhNY6;BFwZ0+_oS$g*vM zh&O?rZv)wu_BK!}*eh6O-S+^wJAfs7fE?Q`h}j85z5^_`MehI&g8hOO)^9IRPzkKs z3#_z#g7~+9q3;5#Y{k1kv*3_mwGDm`DBcCEe-FsBgMy?gVBGt_8e9855U?9KD#*7{ z)j+vmb2VUgM3DM6F!cjqt!?@M2;Bn&eF&_xDIWq=g6)F!7Wfg6`3^AmBj7RHCWzPz z^sE62Ev*Kq-OJ7PyEWWwKWW`R_Dg8%AMhX_*l)1iAM@_Q4aX;xr)-f5cN;3j)~}Yr zt%l0ewoirojD3`6ZG{Rq85C=-=GEdp<<+>!_!KDq0B90$lkpjl^dV668Nf}3AmAe) z@pFKijL(5`fp&mPNSY;azaT&R*#o~I#SKW4q|whVZ$y#~B1MhJZ+`ZxB;b1_@mHkD&z|@dDVKPE zLk|1d#NUwAACOYX5kK?&9SQvrN%bkcgj< zj6YaWk5&AkZ6gRj4ES04VQrhAff|9=!kV>hegSftfwopHXb?mn0Rk=i2vE=n)C<~M z)Sp27uR#8vK#;t1_C zJV2OL2x)ER2l`pIKTz;DP%r3jQC=Ya zD3I?3BCSr)EQo6Z46wX5K=D67lOW1s+X6|)fTFg*Kx+i@1N_+*0gOjmVF2UhjC%ta zA7lxEOpxjUN(F<>(+&vr1yb4pL##wlB?xH`47KF;K&Bs1DHvwK9e@abAfp2iYZZc8 zL3j`_+|q-9TrW@~h_kScKujATrz0@Zss#;#=uW^W%kBgev<2z~V=U?vAYQAIe+n?p z>IBV#xL{y{je>wvfy6U_3@bbXC>MBp0Shgm7m(TwC>1O+Pj4VJ z6iDd}EVdFsl^`SxSYpXxKxTKKQjlfAX95wY0U2ik*;XN_6@>QzmRWiqAh!olBgnC^ zvw)b>ft<5|pdlDytJT3*!0$t1YiDP}~b> z669HI1d!AlD2f2qSfd~y3`pz;5+VPk$h^50KIySZ5`I zDnZD(zsi!Ut9VWK<*_#jiBDb;((Z;Ku#R+omC4O1koe>xef8R zyGHnP8*(X9FZsdWE*Oc#4@2@tB8U9#fTURx7mxhxZx6&H#g`#Xl16{Kd=!!tixiDQ ze)G3qB>|TsiKCGwe|usyQZDh1K@R)d#4$+faHLdn#NT|!BB56xDPxhp{Otuvl_X>w za@60Zk3%x!kV?rhf9pIRi5P)ojAunXRxw`NMi4#$@U!#@+BPGB8iCisuGF@P2Xd|i z+FG@sK@gn)1X^|iP%sLp7qqvii9q~lAb%ndWOagOLEI#uljThUipKy=f?$ii3P>6Y z6kP>$u|`3_I3O_*2(iLMpj_a+8t7&TR|Bczfl@(t^GpUpCjcpvfgV;Os1k%E0X;1_ z3CO$>s1)?F;3+^v0+2BU2(t=7tswjwppT_r1LRHwY6RgHb}bMy3COt?=xf!220`?7 zKtIdA4k)+^s2B9NsHs4FB9K27h_pIEvmowzV1VUa4-{VwGzp?Cb{dd087P_t475f; zKoXF60}yS6Hvr`VZ!$2*5|V+`DL|=Uuz98fq1ON@(}5vYBB&CC%m9X3@(dvJTA)%e z%z|$OBCZ26ZUkbjLQpFRPXUHodJ2#`6{r!!S=dY<=6WD!CNR>f1r37eS->dEo&^+4 z1L_51EGiX`g$@bfD-aV3IWo0%ib-vw=h_ zoDGx#&12hTJEj9y4nhO+V01K>95O6b)cpH#mg|`9a0`Ed# zp(QK?Qs)7sf<@-Z1VV2CQZj+XRwAepge(GIBV#xGZ3`U;=vxK{V)Z2kl z!CLby140)ADa(L$RwAepgxt;bbG_Hvwz z_i+!9dnd5u9-zo}3u3Z>$mPIBTeKW#5bPIhvVOTh!BSvVF7S-)6U1i&LstNsZN&

Z{*gir0zks0+0())6gFv(3 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 VrgI168B~&GQ#mvFfXzkk{{ru-zYYKZ diff --git a/src/app/api/image/delete/route.ts b/src/app/api/image/delete/route.ts new file mode 100644 index 00000000..a38d0e37 --- /dev/null +++ b/src/app/api/image/delete/route.ts @@ -0,0 +1,53 @@ +import { funGetDirectoryNameByValue } from "@/app_modules/_global/fun/get"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; + +export async function DELETE(req: Request) { + const data = await req.json(); + const id = data.fileId; + const dirId = data.dirId; + + const keyOfDirectory = await funGetDirectoryNameByValue({ + value: dirId, + }); + + if (req.method === "DELETE") { + try { + const res = await fetch( + `https://wibu-storage.wibudev.com/api/files/${id}/delete`, + { + method: "DELETE", + headers: { + Authorization: `Bearer ${process.env.WS_APIKEY}`, + }, + } + ); + backendLogger.info("Server status code: " + res.status); + const data = await res.json(); + if (res.ok) { + backendLogger.info( + `Success delete ${keyOfDirectory}` + ); + return NextResponse.json({ success: true }); + } else { + const errorText = await res.json(); + backendLogger.error( + `Failed delete ${keyOfDirectory}: ` + errorText.message + ); + return NextResponse.json({ + success: false, + message: errorText.message, + }); + } + } catch (error) { + backendLogger.error(`Delete error ${keyOfDirectory}:`, error); + return NextResponse.json({ + success: false, + message: "An unexpected error occurred", + }); + } + } else { + backendLogger.error(`Error upload ${keyOfDirectory}: Method not allowed`); + return NextResponse.json({ success: false, message: "Method not allowed" }); + } +} diff --git a/src/app/api/image/upload/route.ts b/src/app/api/image/upload/route.ts new file mode 100644 index 00000000..a1004088 --- /dev/null +++ b/src/app/api/image/upload/route.ts @@ -0,0 +1,58 @@ +import { funGetDirectoryNameByValue } from "@/app_modules/_global/fun/get"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; +export async function POST(request: Request) { + const formData = await request.formData(); + + const valueOfDir = formData.get("dirId"); + const keyOfDirectory = await funGetDirectoryNameByValue({ + value: valueOfDir as string, + }); + + if (request.method === "POST") { + try { + const res = await fetch("https://wibu-storage.wibudev.com/api/upload", { + method: "POST", + body: formData, + headers: { + Authorization: `Bearer ${process.env.WS_APIKEY}`, + }, + }); + + backendLogger.info("Server status code: " + res.status); + const dataRes = await res.json(); + + if (res.ok) { + backendLogger.info( + `Success upload ${keyOfDirectory}: ${JSON.stringify(dataRes.data)}` + ); + return NextResponse.json( + { success: true, data: dataRes.data }, + { status: 200 } + ); + } else { + const errorText = await res.text(); + backendLogger.error(`Failed upload ${keyOfDirectory}: ${errorText}`); + return NextResponse.json( + { success: false, message: errorText }, + { status: 400 } + ); + } + } catch (error) { + backendLogger.error(`Error upload ${keyOfDirectory}: ${error}`); + return NextResponse.json( + { + success: false, + message: "An unexpected error occurred", + }, + { status: 500 } + ); + } + } else { + backendLogger.error(`Error upload ${keyOfDirectory}: Method not allowed`); + return NextResponse.json( + { success: false, message: "Method not allowed" }, + { status: 405 } + ); + } +} diff --git a/src/app/api/upload/route.ts b/src/app/api/upload/route.ts deleted file mode 100644 index 00e2b69c..00000000 --- a/src/app/api/upload/route.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { NextResponse } from "next/server"; -export async function POST(request: Request) { - const WS_APIKEY = process.env.WS_APIKEY; - console.log(WS_APIKEY); - - try { - const formData = await request.formData(); - - const res = await fetch("https://wibu-storage.wibudev.com/api/upload", { - method: "POST", - body: formData, - headers: { - Authorization: `Bearer ${process.env.WS_APIKEY}`, - }, - }); - - // if (res.ok) { - // console.log("Berhasil"); - // const hasil = await res.json(); - // return { success: true, data: hasil.data }; - // } else { - // const errorText = await res.text(); - // return { success: false, data: {} }; - // } - } catch (error) { - console.log(error); - } - - // try { - // const res = await fetch("https://wibu-storage.wibudev.com/api/upload", { - // method: "POST", - // body: formData, - // headers: { - // Authorization: `Bearer ${process.env.WS_APIKEY}`, - // }, - // }); - - // if (res.ok) { - // const hasil = await res.json(); - // return { success: true, data: hasil.data }; - // } else { - // const errorText = await res.text(); - // return { success: false, data: {} }; - // } - // } catch (error) { - // console.error("Upload error:", error); - // return { success: false, data: {} }; - // } - - return NextResponse.json({ success: true }); -} diff --git a/src/app/zCoba/page.tsx b/src/app/zCoba/page.tsx index 8df38920..dd5cb2f4 100644 --- a/src/app/zCoba/page.tsx +++ b/src/app/zCoba/page.tsx @@ -33,7 +33,7 @@ export default function Page() { const formData = new FormData(); formData.append("file", filePP as any); - const res = await fetch("/api/upload", { + const res = await fetch("/api/image/upload", { method: "POST", body: formData, }); diff --git a/src/app_modules/_global/fun/delete/fun_delete_file_by_id.tsx b/src/app_modules/_global/fun/delete/fun_delete_file_by_id.tsx index a31df82d..cb3d48be 100644 --- a/src/app_modules/_global/fun/delete/fun_delete_file_by_id.tsx +++ b/src/app_modules/_global/fun/delete/fun_delete_file_by_id.tsx @@ -1,24 +1,50 @@ -export async function funGlobal_DeleteFileById({ fileId }: { fileId: string }) { - try { - const res = await fetch( - `https://wibu-storage.wibudev.com/api/files/${fileId}/delete`, - { - method: "DELETE", - headers: { - Authorization: `Bearer ${process.env.WS_APIKEY}`, - }, - } - ); +import { clientLogger } from "@/util/clientLogger"; - if (res.ok) { - const hasil = await res.json(); - return { success: true }; +export async function funGlobal_DeleteFileById({ + fileId, + dirId, +}: { + fileId: string; + dirId?: string; +}) { + try { + const res = await fetch("/api/image/delete", { + method: "DELETE", + body: JSON.stringify({ fileId, dirId }), + }); + + const data = await res.json(); + + if (data.success) { + clientLogger.info(`File ${fileId} deleted successfully`); + return { success: true, message: "File berhasil dihapus" }; } else { - const errorText = await res.json(); - return { success: false }; + return { success: false, message: data.message }; } } catch (error) { - return { success: false }; console.error("Upload error:", error); + return { success: false, message: "An unexpected error occurred" }; } + // try { + // const res = await fetch( + // `https://wibu-storage.wibudev.com/api/files/${fileId}/delete`, + // { + // method: "DELETE", + // headers: { + // Authorization: `Bearer ${process.env.WS_APIKEY}`, + // }, + // } + // ); + + // if (res.ok) { + // const hasil = await res.json(); + // return { success: true, message: "File berhasil dihapus" }; + // } else { + // const errorText = await res.json(); + // return { success: false, message: errorText.message }; + // } + // } catch (error) { + // console.error("Upload error:", error); + // return { success: false, message: "An unexpected error occurred" }; + // } } diff --git a/src/app_modules/_global/fun/get/fun_get_directory_name.ts b/src/app_modules/_global/fun/get/fun_get_directory_name.ts new file mode 100644 index 00000000..fbacfec2 --- /dev/null +++ b/src/app_modules/_global/fun/get/fun_get_directory_name.ts @@ -0,0 +1,11 @@ +import { DIRECTORY_ID } from "@/app/lib"; + +export async function funGetDirectoryNameByValue({ + value, +}: { + value?: string | null; +}) { + if (!value) return null; + const object: any = DIRECTORY_ID; + return Object.keys(object).find((key) => object[key] === value); +} diff --git a/src/app_modules/_global/fun/get/index.ts b/src/app_modules/_global/fun/get/index.ts index b71aacbe..25d8ace9 100644 --- a/src/app_modules/_global/fun/get/index.ts +++ b/src/app_modules/_global/fun/get/index.ts @@ -1,5 +1,5 @@ - import { funGlobal_CheckProfile } from "./fun_check_profile"; +import { funGetDirectoryNameByValue } from "./fun_get_directory_name"; import { funGlobal_getNomorAdmin } from "./fun_get_nomor_admin"; import { funGetUserIdByToken } from "./fun_get_user_id_by_token"; import { funGlobal_getMasterKategoriApp } from "./fun_master_kategori_app"; @@ -8,3 +8,4 @@ export { funGlobal_getMasterKategoriApp }; export { funGlobal_getNomorAdmin }; export { funGetUserIdByToken }; export { funGlobal_CheckProfile }; +export { funGetDirectoryNameByValue }; diff --git a/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts b/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts index df5b7377..4af61f50 100644 --- a/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts +++ b/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts @@ -32,40 +32,20 @@ export async function funGlobal_UploadToStorage({ console.error("File terlalu besar"); return { success: false, message: "File size exceeds limit" }; } - - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 30000); // Timeout 30 detik - const formData = new FormData(); formData.append("file", file); formData.append("dirId", dirId); - try { - const res = await fetch("https://wibu-storage.wibudev.com/api/upload", { - method: "POST", - body: formData, - headers: { - Authorization: `Bearer ${Env_WS_APIKEY}`, - }, - signal: controller.signal, - }); + const upload = await fetch("/api/image/upload", { + method: "POST", + body: formData, + }); - clearTimeout(timeoutId); // Bersihkan timeout jika selesai tepat waktu + const res = await upload.json(); - if (res.ok) { - const dataRes = await res.json(); - // const cekLog = await res.text(); - // console.log(cekLog); - return { success: true, data: dataRes.data }; - } else { - const errorText = await res.text(); - console.error("Error:", errorText); - return { success: false, message: errorText }; - } - } catch (error) { - clearTimeout(timeoutId); // - - console.error("Error:", error); - return { success: false, message: "An unexpected error occurred" }; + if (upload.ok) { + return { success: true, data: res.data, message: res.message }; + } else { + return { success: false, data: {}, message: res.message }; } } diff --git a/src/app_modules/katalog/profile/create/view.tsx b/src/app_modules/katalog/profile/create/view.tsx index f0dd96b4..90d51ab2 100644 --- a/src/app_modules/katalog/profile/create/view.tsx +++ b/src/app_modules/katalog/profile/create/view.tsx @@ -1,42 +1,16 @@ "use client"; -import { DIRECTORY_ID } from "@/app/lib"; -import { MainColor } from "@/app_modules/_global/color"; -import { - ComponentGlobal_BoxInformation, - ComponentGlobal_BoxUploadImage, - ComponentGlobal_ErrorInput, -} from "@/app_modules/_global/component"; -import { - funGlobal_DeleteFileById, - funGlobal_UploadToStorage, -} from "@/app_modules/_global/fun"; -import { MAX_SIZE } from "@/app_modules/_global/lib"; -import { PemberitahuanMaksimalFile } from "@/app_modules/_global/lib/max_size"; -import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; -import { - AspectRatio, - Avatar, - Box, - Button, - Center, - FileButton, - Image, - Paper, - Select, - Stack, - Text, - TextInput, -} from "@mantine/core"; -import { IconAt, IconCamera, IconUpload } from "@tabler/icons-react"; +import { ComponentGlobal_ErrorInput } from "@/app_modules/_global/component"; +import { Select, Stack, TextInput } from "@mantine/core"; +import { IconAt } from "@tabler/icons-react"; import { useState } from "react"; import { gmailRegex } from "../../component/regular_expressions"; import { Profile_ComponentCreateNewProfile } from "../_component"; +import Profile_ViewUploadBackground from "./view_upload_background"; +import Profile_ViewUploadFoto from "./view_upload_foto"; export default function CreateProfile() { - const [filePP, setFilePP] = useState(null); const [imgPP, setImgPP] = useState(); - const [fileBG, setFileBG] = useState(null); const [imgBG, setImgBG] = useState(); const [fotoProfileId, setFotoProfileId] = useState(""); const [backgroundProfileId, setBackgroundProfileId] = useState(""); @@ -51,223 +25,19 @@ export default function CreateProfile() { return ( <> - - - -

- {imgPP ? ( - - - - ) : ( - - - - )} -
+ -
- { - try { - const buffer = URL.createObjectURL( - new Blob([new Uint8Array(await files.arrayBuffer())]) - ); - - if (files.size > MAX_SIZE) { - ComponentGlobal_NotifikasiPeringatan( - PemberitahuanMaksimalFile - ); - setImgPP(null); - setFilePP(null); - - return; - } - - if (fotoProfileId != "") { - const deleteFotoProfile = await funGlobal_DeleteFileById({ - fileId: fotoProfileId, - }); - - if (deleteFotoProfile.success) { - setFotoProfileId(""); - - const uploadPhoto = await funGlobal_UploadToStorage({ - file: files, - dirId: DIRECTORY_ID.profile_foto, - }); - - if (uploadPhoto.success) { - setFotoProfileId(uploadPhoto.data.id); - setImgPP(buffer); - setFilePP(files); - } else { - ComponentGlobal_NotifikasiPeringatan( - "Gagal upload foto profile" - ); - } - } - } else { - const uploadPhoto = await funGlobal_UploadToStorage({ - file: files, - dirId: DIRECTORY_ID.profile_foto, - }); - - if (uploadPhoto.success) { - setFotoProfileId(uploadPhoto.data.id); - setImgPP(buffer); - setFilePP(files); - } else { - ComponentGlobal_NotifikasiPeringatan( - "Gagal upload foto profile" - ); - } - } - } catch (error) { - console.log(error); - } - }} - accept="image/png,image/jpeg" - > - {(props) => ( - - )} - -
- - - - - - - - {imgBG ? ( - - Foto - - ) : ( - - - - Upload Background - - - )} - - -
- { - try { - const buffer = URL.createObjectURL( - new Blob([new Uint8Array(await files.arrayBuffer())]) - ); - - if (files.size > MAX_SIZE) { - ComponentGlobal_NotifikasiPeringatan( - PemberitahuanMaksimalFile - ); - setImgBG(null); - setFileBG(null); - return; - } - - if (backgroundProfileId != "") { - const deleteFotoBg = await funGlobal_DeleteFileById({ - fileId: backgroundProfileId, - }); - - if (deleteFotoBg.success) { - setBackgroundProfileId(""); - - const uploadBackground = - await funGlobal_UploadToStorage({ - file: files, - dirId: DIRECTORY_ID.profile_background, - }); - - if (uploadBackground.success) { - setBackgroundProfileId(uploadBackground.data.id); - setImgBG(buffer); - setFileBG(files); - } else { - ComponentGlobal_NotifikasiPeringatan( - "Gagal upload background profile" - ); - } - } - } else { - const uploadBackground = await funGlobal_UploadToStorage({ - file: files, - dirId: DIRECTORY_ID.profile_background, - }); - - if (uploadBackground.success) { - setBackgroundProfileId(uploadBackground.data.id); - setImgBG(buffer); - setFileBG(files); - } else { - ComponentGlobal_NotifikasiPeringatan( - "Gagal upload background profile" - ); - } - } - } catch (error) { - console.log(error); - } - }} - accept="image/png,image/jpeg" - > - {(props) => ( - - )} - -
-
-
+ void; + backgroundProfileId: string; + onSetBackgroundProfileId: (id: string) => void; +}) { + const [isLoading, setLoading] = useState(false); + + return ( + <> + + + + + {isLoading ? ( +
+ +
+ ) : imgBG ? ( + + Foto + + ) : ( + + + + Upload Background + + + )} +
+ +
+ { + try { + setLoading(true); + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + + if (files.size > MAX_SIZE) { + ComponentGlobal_NotifikasiPeringatan( + PemberitahuanMaksimalFile + ); + onSetImgBG(null); + + return; + } + + if (backgroundProfileId != "") { + const deleteFotoBg = await funGlobal_DeleteFileById({ + fileId: backgroundProfileId, + dirId: DIRECTORY_ID.profile_background, + }); + + if (!deleteFotoBg.success) { + clientLogger.error( + "Client failed delete background:" + + deleteFotoBg.message + ); + return; + } + + if (deleteFotoBg.success) { + onSetBackgroundProfileId(""); + onSetImgBG(null); + + const uploadBackground = await funGlobal_UploadToStorage({ + file: files, + dirId: DIRECTORY_ID.profile_background, + }); + + if (!uploadBackground.success) { + clientLogger.error( + "Client failed upload background:" + + uploadBackground.message + ); + return; + } + + if (uploadBackground.success) { + onSetBackgroundProfileId(uploadBackground.data.id); + onSetImgBG(buffer); + } else { + ComponentGlobal_NotifikasiPeringatan( + "Gagal upload background profile" + ); + } + } + } else { + const uploadBackground = await funGlobal_UploadToStorage({ + file: files, + dirId: DIRECTORY_ID.profile_background, + }); + + if (uploadBackground.success) { + onSetBackgroundProfileId(uploadBackground.data.id); + onSetImgBG(buffer); + } else { + ComponentGlobal_NotifikasiPeringatan( + "Gagal upload background profile" + ); + } + } + } catch (error) { + clientLogger.error("Client error upload background:", error); + } finally { + setLoading(false); + } + }} + accept="image/png,image/jpeg" + > + {(props) => ( + + )} + +
+
+
+ + ); +} diff --git a/src/app_modules/katalog/profile/create/view_upload_foto.tsx b/src/app_modules/katalog/profile/create/view_upload_foto.tsx new file mode 100644 index 00000000..279f1282 --- /dev/null +++ b/src/app_modules/katalog/profile/create/view_upload_foto.tsx @@ -0,0 +1,216 @@ +import { DIRECTORY_ID } from "@/app/lib"; +import { MainColor } from "@/app_modules/_global/color"; +import { ComponentGlobal_BoxInformation } from "@/app_modules/_global/component"; +import { + funGlobal_DeleteFileById, + funGlobal_UploadToStorage, +} from "@/app_modules/_global/fun"; +import { MAX_SIZE } from "@/app_modules/_global/lib"; +import { PemberitahuanMaksimalFile } from "@/app_modules/_global/lib/max_size"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { clientLogger } from "@/util/clientLogger"; +import { + Avatar, + Box, + Button, + Center, + FileButton, + Loader, + Paper, + Stack, +} from "@mantine/core"; +import { IconCamera } from "@tabler/icons-react"; +import { useState } from "react"; + +export default function Profile_ViewUploadFoto({ + imgPP, + onSetImgPP, + fotoProfileId, + onSetFotoProfileId, +}: { + imgPP: string | null | undefined; + onSetImgPP: (img: string | null) => void; + fotoProfileId: string; + onSetFotoProfileId: (id: string) => void; +}) { + const [isLoading, setLoading] = useState(false); + + return ( + <> + + + +
+ {isLoading ? ( + + +
+ +
+
+
+ ) : imgPP != undefined || imgPP != null ? ( + + + + ) : ( + + + + )} +
+ +
+ { + try { + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + + if (files.size > MAX_SIZE) { + ComponentGlobal_NotifikasiPeringatan( + PemberitahuanMaksimalFile + ); + onSetImgPP(null); + + return; + } + + if (fotoProfileId != "") { + try { + setLoading(true); + const deleteFotoProfile = await funGlobal_DeleteFileById({ + fileId: fotoProfileId, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (!deleteFotoProfile.success) { + clientLogger.error( + "Client failed delete photo profile:" + + deleteFotoProfile.message + ); + + return; + } + + if (deleteFotoProfile.success) { + onSetFotoProfileId(""); + onSetImgPP(null); + + const uploadPhoto = await funGlobal_UploadToStorage({ + file: files, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (!uploadPhoto.success) { + clientLogger.error( + "Client failed upload photo profile::" + + uploadPhoto.message + ); + return; + } + + if (uploadPhoto.success) { + clientLogger.info( + "Client success upload foto profile" + ); + onSetFotoProfileId(uploadPhoto.data.id); + onSetImgPP(buffer); + } else { + clientLogger.error( + "Client failed upload foto:", + uploadPhoto.message + ); + ComponentGlobal_NotifikasiPeringatan( + "Gagal upload foto profile" + ); + } + } + } catch (error) { + clientLogger.error("Client error upload foto:", error); + } finally { + setLoading(false); + } + } else { + try { + setLoading(true); + const uploadPhoto = await funGlobal_UploadToStorage({ + file: files, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (uploadPhoto.success) { + clientLogger.info("Client success upload foto profile"); + onSetFotoProfileId(uploadPhoto.data.id); + onSetImgPP(buffer); + } else { + clientLogger.error( + "Client failed upload foto:", + uploadPhoto.message + ); + ComponentGlobal_NotifikasiPeringatan( + "Gagal upload foto profile" + ); + } + } catch (error) { + clientLogger.error("Client error upload foto:", error); + } finally { + setLoading(false); + } + } + } catch (error) { + clientLogger.error("Client error upload foto:", error); + } + }} + accept="image/png,image/jpeg" + > + {(props) => ( + + )} + +
+
+
+ + ); +} diff --git a/src/app_modules/katalog/profile/fun/fun_create_profile.ts b/src/app_modules/katalog/profile/fun/fun_create_profile.ts index ff8eb96d..35af0660 100644 --- a/src/app_modules/katalog/profile/fun/fun_create_profile.ts +++ b/src/app_modules/katalog/profile/fun/fun_create_profile.ts @@ -3,6 +3,7 @@ import prisma from "@/app/lib/prisma"; import { RouterHome } from "@/app/lib/router_hipmi/router_home"; import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import backendLogger from "@/util/backendLogger"; import { Prisma } from "@prisma/client"; import { revalidatePath } from "next/cache"; @@ -19,6 +20,7 @@ export default async function funCreateNewProfile({ const userLoginId = await funGetUserIdByToken(); if (!userLoginId) { + backendLogger.error("User tidak terautentikasi"); return { status: 400, message: "User tidak terautentikasi" }; // Validasi user login } @@ -45,6 +47,7 @@ export default async function funCreateNewProfile({ }); if (!createProfile) { + backendLogger.error("Gagal membuat profile"); return { status: 400, message: "Gagal membuat profile" }; } @@ -61,7 +64,7 @@ export default async function funCreateNewProfile({ message: "Berhasil", }; } catch (error) { - console.error("Error creating profile:", error); + backendLogger.error("Terjadi kesalahan pada server", error); return { status: 500, message: "Terjadi kesalahan pada server" }; } } diff --git a/src/app_modules/zCoba/ui_coba_upload_file.tsx b/src/app_modules/zCoba/ui_coba_upload_file.tsx index 091a09fa..eb6eb30e 100644 --- a/src/app_modules/zCoba/ui_coba_upload_file.tsx +++ b/src/app_modules/zCoba/ui_coba_upload_file.tsx @@ -189,7 +189,7 @@ async function coba_ButtonFileUpload({ formData.append("dirId", dirId); try { - const res = await fetch("https://wibu-storage.wibudev.com/api/upload", { + const res = await fetch("https://wibu-storage.wibudev.com/api/image/upload", { method: "POST", body: formData, headers: { diff --git a/src/middleware.ts b/src/middleware.ts index 705192f2..6a72d076 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -19,7 +19,8 @@ const middlewareConfig: MiddlewareConfig = { userPath: "/dev/home", publicRoutes: [ "/", - "/api/upload", + "/api/logs/*", + "/api/image/*", "/api/job/*", "/api/validation", "/api/auth/*", diff --git a/src/util/backendLogger.ts b/src/util/backendLogger.ts new file mode 100644 index 00000000..9a8e3d30 --- /dev/null +++ b/src/util/backendLogger.ts @@ -0,0 +1,37 @@ +// src/utils/backendLogger.ts +import winston from "winston"; +import DailyRotateFile from "winston-daily-rotate-file"; +import path from "path"; + +const backendLogger = winston.createLogger({ + level: "info", + format: winston.format.combine( + winston.format.timestamp(), + winston.format.json() + ), + transports: [ + // Error logs + new DailyRotateFile({ + filename: path.join("logs/backend/error-%DATE%.log"), + datePattern: "YYYY-MM-DD", + level: "error", + zippedArchive: true, + maxSize: "20m", + maxFiles: "14d", + }), + // Combined logs + new DailyRotateFile({ + filename: path.join("logs/backend/combined-%DATE%.log"), + datePattern: "YYYY-MM-DD", + zippedArchive: true, + maxSize: "20m", + maxFiles: "14d", + }), + // Console output in development + ...(process.env.NODE_ENV !== "production" + ? [new winston.transports.Console()] + : []), + ], +}); + +export default backendLogger; diff --git a/src/util/clientLogger.ts b/src/util/clientLogger.ts new file mode 100644 index 00000000..4782df23 --- /dev/null +++ b/src/util/clientLogger.ts @@ -0,0 +1,87 @@ +// src/utils/clientLogger.ts +interface LogEntry { + level: "info" | "warn" | "error"; + message: string; + data?: any; + timestamp?: string; +} + +class ClientLogger { + private queue: LogEntry[] = []; + private readonly maxQueueSize: number = 10; + private readonly apiEndpoint: string = "/api/logs"; + private isSending: boolean = false; + + private async sendLogs(): Promise { + if (this.isSending || this.queue.length === 0) return; + + this.isSending = true; + const logsToSend = [...this.queue]; + this.queue = []; + + try { + const response = await fetch(this.apiEndpoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(logsToSend), + }); + + if (!response.ok) { + console.error("Failed to send logs:", response.statusText); + // Restore logs to queue if send failed + this.queue = [...logsToSend, ...this.queue]; + } + } catch (error) { + console.error("Error sending logs:", error); + // Restore logs to queue if send failed + this.queue = [...logsToSend, ...this.queue]; + } finally { + this.isSending = false; + } + } + + private addToQueue(entry: LogEntry): void { + this.queue.push({ + ...entry, + timestamp: new Date().toISOString(), + }); + + if (this.queue.length >= this.maxQueueSize) { + this.sendLogs(); + } + } + + public info(message: string, data?: any): void { + this.addToQueue({ level: "info", message, data }); + } + + public warn(message: string, data?: any): void { + this.addToQueue({ level: "warn", message, data }); + // Send immediately for warnings + this.sendLogs(); + } + + public error(message: string, error?: Error | any): void { + const errorData = + error instanceof Error + ? { + name: error.name, + message: error.message, + stack: error.stack, + } + : error; + + this.addToQueue({ level: "error", message, data: errorData }); + // Send immediately for errors + this.sendLogs(); + } + + // Flush remaining logs (useful when page is about to unload) + public flush(): void { + this.sendLogs(); + } +} + +export const clientLogger = new ClientLogger(); diff --git a/src/util/frontend-logger.ts b/src/util/frontend-logger.ts new file mode 100644 index 00000000..6eff4277 --- /dev/null +++ b/src/util/frontend-logger.ts @@ -0,0 +1,101 @@ +// utils/frontend-logger.ts +import winston from "winston"; +import DailyRotateFile from "winston-daily-rotate-file"; +import path from "path"; + +// Define log levels and their priorities +const levels = { + error: 0, + warn: 1, + info: 2, + debug: 3, +}; + +// Define interface for log entries +export interface LogEntry { + level: keyof typeof levels; + message: string; + data?: any; + timestamp?: string; + userAgent?: string; + ip?: string; + url?: string; +} + +// Custom format for log entries +const logFormat = winston.format.combine( + winston.format.timestamp({ + format: "YYYY-MM-DD HH:mm:ss", + }), + winston.format.metadata({ fillExcept: ["message", "level", "timestamp"] }), + winston.format.json() +); + +// Create the logger instance +const frontendLogger: winston.Logger = winston.createLogger({ + levels, + level: process.env.LOG_LEVEL || "info", + format: logFormat, + transports: [ + // Daily Rotate File for errors + new DailyRotateFile({ + filename: path.join(process.cwd(), "logs/frontend/error-%DATE%.log"), + datePattern: "YYYY-MM-DD", + zippedArchive: true, + maxSize: "20m", + maxFiles: "14d", + level: "error", + format: logFormat, + }), + + // Daily Rotate File for all logs + new DailyRotateFile({ + filename: path.join(process.cwd(), "logs/frontend/combined-%DATE%.log"), + datePattern: "YYYY-MM-DD", + zippedArchive: true, + maxSize: "20m", + maxFiles: "14d", + format: logFormat, + }), + ], + // Handle errors from the logger itself + exitOnError: false, +}); + +// Add console transport in development +if (process.env.NODE_ENV !== "production") { + frontendLogger.add( + new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.simple() + ), + }) + ); +} + +// Helper functions for type-safe logging +export function logError(message: string, data?: any) { + frontendLogger.error(message, { data }); +} + +export function logWarn(message: string, data?: any) { + frontendLogger.warn(message, { data }); +} + +export function logInfo(message: string, data?: any) { + frontendLogger.info(message, { data }); +} + +export function logDebug(message: string, data?: any) { + frontendLogger.debug(message, { data }); +} + +// Helper function for dynamic logging +export function log(entry: LogEntry) { + const { level, message, ...metadata } = entry; + frontendLogger[level](message, metadata); +} + +// Export the logger instance as default +export default frontendLogger; From b5375f272c35c956f5fe5db984a857885c196c31 Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 16 Dec 2024 17:40:52 +0800 Subject: [PATCH 10/23] upd : investasi portofolio Deskripsi: - api pada list investasi portofolio - semua status No Issues --- src/app/api/new/investasi/route.ts | 32 +++++++ .../investasi/main/portofolio/[id]/page.tsx | 17 ++-- .../comp_card_portofolio_not_publish_new.tsx | 44 +++++++++ .../main/comp_card_portofolio_publish_new.tsx | 89 +++++++++++++++++++ .../investasi/_lib/type_investasi.ts | 2 + src/app_modules/investasi/_ui/index.ts | 2 + .../investasi/_ui/main/ui_portofolio_new.tsx | 56 ++++++++++++ .../main/portofolio/skeleton_portofolio.tsx | 31 +++++++ .../main/portofolio/view_portofolio_new.tsx | 79 ++++++++++++++++ .../_view/main/view_portofolio_new.tsx | 0 10 files changed, 344 insertions(+), 8 deletions(-) create mode 100644 src/app_modules/investasi/_component/main/comp_card_portofolio_not_publish_new.tsx create mode 100644 src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx create mode 100644 src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx create mode 100644 src/app_modules/investasi/_view/main/portofolio/skeleton_portofolio.tsx create mode 100644 src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx create mode 100644 src/app_modules/investasi/_view/main/view_portofolio_new.tsx diff --git a/src/app/api/new/investasi/route.ts b/src/app/api/new/investasi/route.ts index a8939023..ea13150c 100644 --- a/src/app/api/new/investasi/route.ts +++ b/src/app/api/new/investasi/route.ts @@ -98,6 +98,38 @@ export async function GET(request: Request) { if (userLoginId == null) { return NextResponse.json({ success: false, message: "Gagal mendapatkan data, user id tidak ada" }, { status: 500 }); } + + const data = await prisma.investasi.findMany({ + take: 5, + skip: dataSkip, + orderBy: { + updatedAt: "desc", + }, + where: { + authorId: userLoginId, + masterStatusInvestasiId: status, + }, + select: { + id: true, + title: true, + targetDana: true, + imageId: true, + countDown: true, + updatedAt: true, + MasterPencarianInvestor: { + select: { + name: true + } + } + } + }); + + dataFix = data.map((v: any) => ({ + ..._.omit(v, ["MasterPencarianInvestor"]), + pencarianInvestor: v.MasterPencarianInvestor.name + })) + + } diff --git a/src/app/dev/investasi/main/portofolio/[id]/page.tsx b/src/app/dev/investasi/main/portofolio/[id]/page.tsx index b8378bd8..b9307680 100644 --- a/src/app/dev/investasi/main/portofolio/[id]/page.tsx +++ b/src/app/dev/investasi/main/portofolio/[id]/page.tsx @@ -1,22 +1,23 @@ import { investasi_funGetPortofolioByStatusId } from "@/app_modules/investasi/_fun"; -import { Investasi_UiPortofolio } from "@/app_modules/investasi/_ui"; +import { Investasi_UiPortofolio, Investasi_UiPortofolioNew } from "@/app_modules/investasi/_ui"; import getStatusInvestasi from "@/app_modules/investasi/fun/master/get_status_investasi"; export default async function Page({ params }: { params: { id: string } }) { - const statusId = params.id; + // const statusId = params.id; const listStatus = await getStatusInvestasi(); - const dataPortofolio = await investasi_funGetPortofolioByStatusId({ - page: 1, - statusId: statusId, - }); + // const dataPortofolio = await investasi_funGetPortofolioByStatusId({ + // page: 1, + // statusId: statusId, + // }); return ( <> - + /> */} + ); } diff --git a/src/app_modules/investasi/_component/main/comp_card_portofolio_not_publish_new.tsx b/src/app_modules/investasi/_component/main/comp_card_portofolio_not_publish_new.tsx new file mode 100644 index 00000000..c87fc3a9 --- /dev/null +++ b/src/app_modules/investasi/_component/main/comp_card_portofolio_not_publish_new.tsx @@ -0,0 +1,44 @@ +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { ComponentGlobal_CardStyles, ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; +import { Grid, Stack, Text } from "@mantine/core"; +import _ from "lodash"; +import { useRouter } from "next/navigation"; +import { IDataInvestasiBursa } from "../../_lib/type_investasi"; + +export function Investasi_ComponentCardPortofolio_NotPublishNew({ data, }: { data: IDataInvestasiBursa; }) { + const router = useRouter(); + return ( + <> + { + router.push(NEW_RouterInvestasi.detail_portofolio({ id: data.id }), { scroll: false }); + }} + > + + + + {" "} + {_.startCase(data.title)} + + + Target Dana: + + Rp.{" "} + {new Intl.NumberFormat("id-ID", { + maximumSignificantDigits: 10, + }).format(+data.targetDana)} + + + + + + + + + + + ); +} diff --git a/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx b/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx new file mode 100644 index 00000000..8366a163 --- /dev/null +++ b/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx @@ -0,0 +1,89 @@ +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { ComponentGlobal_CardLoadingOverlay, ComponentGlobal_LoadImageCustom, } from "@/app_modules/_global/component"; +import { Box, Grid, Group, Stack, Text } from "@mantine/core"; +import { IconChecklist, IconCircleCheck } from "@tabler/icons-react"; +import _ from "lodash"; +import moment from "moment"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { IDataInvestasiBursa } from "../../_lib/type_investasi"; +import { Investasi_ComponentStylesCard } from "../comp_card_border_and_background"; + +export function Investasi_ComponentCardPortofolioPublishNew({ data }: { data: IDataInvestasiBursa; }) { + const router = useRouter(); + const [visible, setVisible] = useState(false); + + return ( + <> + { + router.push(NEW_RouterInvestasi.detail_main({ id: data?.id }), { + scroll: false, + }); + setVisible(true); + }} + > + + + {_.capitalize(data?.title)} + + Target Dana: + + Rp.{" "} + {new Intl.NumberFormat("id-ID", { + maximumSignificantDigits: 10, + }).format(+data?.targetDana)} + + + + + + + + + + + {Number(data?.pencarianInvestor) - + moment(new Date()).diff(new Date(data?.updatedAt), "days") <= + 0 ? ( + + + Selesai + + ) : ( + + + {Number(data?.pencarianInvestor) - + moment(new Date()).diff(new Date(data?.countDown), "days") <= + 0 ? ( + + + Selesai + + ) : ( + + Sisa Waktu : {} + {Number(data?.pencarianInvestor) - + moment(new Date()).diff( + new Date(data?.countDown), + "days" + )}{" "} + hari + + )} + + + )} + + {visible && } + + + ); +} diff --git a/src/app_modules/investasi/_lib/type_investasi.ts b/src/app_modules/investasi/_lib/type_investasi.ts index b794fb1b..1f8433b3 100644 --- a/src/app_modules/investasi/_lib/type_investasi.ts +++ b/src/app_modules/investasi/_lib/type_investasi.ts @@ -20,5 +20,7 @@ export interface IDataInvestasiBursa { imageId: string progress: string countDown: string + targetDana: string pencarianInvestor: string + updatedAt: Date } \ No newline at end of file diff --git a/src/app_modules/investasi/_ui/index.ts b/src/app_modules/investasi/_ui/index.ts index 6d51522c..956c4bef 100644 --- a/src/app_modules/investasi/_ui/index.ts +++ b/src/app_modules/investasi/_ui/index.ts @@ -26,6 +26,7 @@ import { Investasi_UiCreateBerita } from "./create/ui_create_berita"; import { Investasi_UiDetailBerita } from "./detail/ui_berita"; import { Investasi_UiEditInvestasiNew } from "./edit/ui_edit_investasi_new"; import { Investasi_ViewBerandaNew } from "../_view/main/view_beranda_new"; +import { Investasi_UiPortofolioNew } from "./main/ui_portofolio_new"; export { Investasi_UiProsesPembelian }; export { Investasi_UiMetodePembayaran }; @@ -55,3 +56,4 @@ export { Investasi_UiCreateBerita }; export { Investasi_UiDetailBerita }; export { Investasi_UiEditInvestasiNew } export { Investasi_ViewBerandaNew } +export { Investasi_UiPortofolioNew } diff --git a/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx b/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx new file mode 100644 index 00000000..e21c6d28 --- /dev/null +++ b/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx @@ -0,0 +1,56 @@ +"use client"; +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import { MODEL_NEW_DEFAULT_MASTER } from "@/app_modules/model_global/interface"; +import { Stack, Tabs } from "@mantine/core"; +import { useParams, useRouter } from "next/navigation"; +import { Investasi_ViewPortofolioNew } from "../../_view/main/portofolio/view_portofolio_new"; + +export function Investasi_UiPortofolioNew({ listStatus }: { listStatus: MODEL_NEW_DEFAULT_MASTER[] }) { + const param = useParams<{ id: string }>(); + const router = useRouter(); + + return ( + <> + { + router.push(NEW_RouterInvestasi.portofolio({ id: val })); + }} + > + + + {listStatus.map((e) => ( + + {e.name} + + ))} + + + + + + + ); +} diff --git a/src/app_modules/investasi/_view/main/portofolio/skeleton_portofolio.tsx b/src/app_modules/investasi/_view/main/portofolio/skeleton_portofolio.tsx new file mode 100644 index 00000000..8fc3463b --- /dev/null +++ b/src/app_modules/investasi/_view/main/portofolio/skeleton_portofolio.tsx @@ -0,0 +1,31 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Box, Grid, Skeleton } from "@mantine/core"; + +export default function SkeletonInvestasiPortofolio() { + return ( + <> + {[...Array(4)].map((_, index) => ( + + + + + {[...Array(3)].map((_, i) => ( + + + + + + + + ))} + + + + + + + + ))} + + ); +} \ No newline at end of file diff --git a/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx b/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx new file mode 100644 index 00000000..417c0431 --- /dev/null +++ b/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx @@ -0,0 +1,79 @@ +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; +import { Investasi_ComponentCardPortofolio_NotPublishNew } from "@/app_modules/investasi/_component/main/comp_card_portofolio_not_publish_new"; +import { Investasi_ComponentCardPortofolioPublishNew } from "@/app_modules/investasi/_component/main/comp_card_portofolio_publish_new"; +import { apiGetAllInvestasi } from "@/app_modules/investasi/_lib/api_interface"; +import { IDataInvestasiBursa } from "@/app_modules/investasi/_lib/type_investasi"; +import { Box, Center } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import SkeletonInvestasiPortofolio from "./skeleton_portofolio"; + +export function Investasi_ViewPortofolioNew() { + const param = useParams<{ id: string }>(); + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true) + + async function getDataInvestasi() { + try { + setLoading(true) + const response = await apiGetAllInvestasi(`?cat=portofolio&status=${param.id}&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataInvestasi() + }, []); + + return ( + <> + + { + loading ? + + : + _.isEmpty(data) ? ( + + ) : ( + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllInvestasi(`?cat=portofolio&status=${param.id}&page=${pageNew}`) + setActivePage((val) => val + 1); + + return loadData; + }} + > + { + param.id == "1" ? + (item) => () + : + (item) => () + } +
+ ) + } +
+ + ); +} diff --git a/src/app_modules/investasi/_view/main/view_portofolio_new.tsx b/src/app_modules/investasi/_view/main/view_portofolio_new.tsx new file mode 100644 index 00000000..e69de29b From b8fcbfb2dff80b088bcd411f95c2465bd6ba4b66 Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 16 Dec 2024 17:52:31 +0800 Subject: [PATCH 11/23] upd: investasi - portofolio Deskripsi: - ketinggalan : matiin loading - hapus file yg ga jadi kepake No Issues --- .../_component/main/comp_card_portofolio_publish_new.tsx | 6 +----- .../investasi/_view/main/view_portofolio_new.tsx | 0 2 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 src/app_modules/investasi/_view/main/view_portofolio_new.tsx diff --git a/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx b/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx index 8366a163..61e0e9e5 100644 --- a/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx +++ b/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx @@ -1,17 +1,15 @@ import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; -import { ComponentGlobal_CardLoadingOverlay, ComponentGlobal_LoadImageCustom, } from "@/app_modules/_global/component"; +import { ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; import { Box, Grid, Group, Stack, Text } from "@mantine/core"; import { IconChecklist, IconCircleCheck } from "@tabler/icons-react"; import _ from "lodash"; import moment from "moment"; import { useRouter } from "next/navigation"; -import { useState } from "react"; import { IDataInvestasiBursa } from "../../_lib/type_investasi"; import { Investasi_ComponentStylesCard } from "../comp_card_border_and_background"; export function Investasi_ComponentCardPortofolioPublishNew({ data }: { data: IDataInvestasiBursa; }) { const router = useRouter(); - const [visible, setVisible] = useState(false); return ( <> @@ -21,7 +19,6 @@ export function Investasi_ComponentCardPortofolioPublishNew({ data }: { data: ID router.push(NEW_RouterInvestasi.detail_main({ id: data?.id }), { scroll: false, }); - setVisible(true); }} > @@ -82,7 +79,6 @@ export function Investasi_ComponentCardPortofolioPublishNew({ data }: { data: ID )} - {visible && } ); diff --git a/src/app_modules/investasi/_view/main/view_portofolio_new.tsx b/src/app_modules/investasi/_view/main/view_portofolio_new.tsx deleted file mode 100644 index e69de29b..00000000 From c4b2a47c42763d7327d1616fa0bd036e517b7da2 Mon Sep 17 00:00:00 2001 From: amel Date: Tue, 17 Dec 2024 10:44:14 +0800 Subject: [PATCH 12/23] upd: investasi portofolio Deskripsi: - ganti server action status portofolio menjadi data json No Issues --- .../investasi/main/portofolio/[id]/page.tsx | 8 +++--- .../investasi/_ui/main/ui_portofolio_new.tsx | 27 ++++++++++++++++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/app/dev/investasi/main/portofolio/[id]/page.tsx b/src/app/dev/investasi/main/portofolio/[id]/page.tsx index b9307680..4b135800 100644 --- a/src/app/dev/investasi/main/portofolio/[id]/page.tsx +++ b/src/app/dev/investasi/main/portofolio/[id]/page.tsx @@ -1,10 +1,8 @@ -import { investasi_funGetPortofolioByStatusId } from "@/app_modules/investasi/_fun"; -import { Investasi_UiPortofolio, Investasi_UiPortofolioNew } from "@/app_modules/investasi/_ui"; -import getStatusInvestasi from "@/app_modules/investasi/fun/master/get_status_investasi"; +import { Investasi_UiPortofolioNew } from "@/app_modules/investasi/_ui"; export default async function Page({ params }: { params: { id: string } }) { // const statusId = params.id; - const listStatus = await getStatusInvestasi(); + // const listStatus = await getStatusInvestasi(); // const dataPortofolio = await investasi_funGetPortofolioByStatusId({ // page: 1, // statusId: statusId, @@ -17,7 +15,7 @@ export default async function Page({ params }: { params: { id: string } }) { listStatus={listStatus as any} dataPortofolio={dataPortofolio as any} /> */} - + ); } diff --git a/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx b/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx index e21c6d28..05b97ad2 100644 --- a/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx +++ b/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx @@ -1,14 +1,35 @@ "use client"; import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; import { AccentColor, MainColor } from "@/app_modules/_global/color"; -import { MODEL_NEW_DEFAULT_MASTER } from "@/app_modules/model_global/interface"; import { Stack, Tabs } from "@mantine/core"; import { useParams, useRouter } from "next/navigation"; import { Investasi_ViewPortofolioNew } from "../../_view/main/portofolio/view_portofolio_new"; -export function Investasi_UiPortofolioNew({ listStatus }: { listStatus: MODEL_NEW_DEFAULT_MASTER[] }) { +export function Investasi_UiPortofolioNew() { const param = useParams<{ id: string }>(); const router = useRouter(); + const status = [ + { + id: "1", + name: "Publish", + color: "green" + }, + { + id: "2", + name: "Review", + color: "orange" + }, + { + id: "3", + name: "Draft", + color: "yellow" + }, + { + id: "4", + name: "Reject", + color: "red" + } + ] return ( <> @@ -30,7 +51,7 @@ export function Investasi_UiPortofolioNew({ listStatus }: { listStatus: MODEL_NE > - {listStatus.map((e) => ( + {status.map((e) => ( Date: Tue, 17 Dec 2024 10:56:25 +0800 Subject: [PATCH 13/23] fix: donasi Deskripsi: - beranda = salah codingan load saat ada data baru No Issues --- src/app_modules/donasi/main/beranda_new.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app_modules/donasi/main/beranda_new.tsx b/src/app_modules/donasi/main/beranda_new.tsx index 04385702..f6d1b66c 100644 --- a/src/app_modules/donasi/main/beranda_new.tsx +++ b/src/app_modules/donasi/main/beranda_new.tsx @@ -37,7 +37,7 @@ export default function MainDonasiNew() { async function onLoadData({ onPublish }: { onPublish: (val: any) => void }) { setIsLoading(true); const loadData = await apiGetAllDonasi(`?cat=beranda&page=1`) - onPublish(loadData); + onPublish(loadData.data) setIsShowUpdate(false); setIsTriggerDonasiBeranda(false); From 11b938314163b67c4dce92cb8adce35a068d084f Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 17 Dec 2024 11:12:27 +0800 Subject: [PATCH 14/23] Logs Deskripsi: - Fitur logs untuk update foto profile - Fitu logs untuk update background profile --- src/app/dev/notifikasi/collaboration/page.tsx | 4 +- src/app/lib/prisma.ts | 22 +++--- .../button/comp_button_upload_photo.tsx | 3 +- src/app_modules/auth/register/view.tsx | 6 +- src/app_modules/auth/validasi/view.tsx | 41 ++++++----- .../comp_button_update_background_profile.tsx | 70 +++++++++++++------ .../comp_button_update_photo_profile.tsx | 62 ++++++++++------ .../profile/upload/foto_background/index.tsx | 1 + .../profile/upload/foto_profile/index.tsx | 1 + 9 files changed, 129 insertions(+), 81 deletions(-) diff --git a/src/app/dev/notifikasi/collaboration/page.tsx b/src/app/dev/notifikasi/collaboration/page.tsx index 78f26b83..edf580a3 100644 --- a/src/app/dev/notifikasi/collaboration/page.tsx +++ b/src/app/dev/notifikasi/collaboration/page.tsx @@ -1,6 +1,4 @@ -import { - Notifikasi_UiCollaboration -} from "@/app_modules/notifikasi/_ui"; +import { Notifikasi_UiCollaboration } from "@/app_modules/notifikasi/_ui"; import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; export default async function Page() { diff --git a/src/app/lib/prisma.ts b/src/app/lib/prisma.ts index 25a08ef9..07ccb3e8 100644 --- a/src/app/lib/prisma.ts +++ b/src/app/lib/prisma.ts @@ -1,28 +1,30 @@ -import { PrismaClient } from '@prisma/client'; +import { PrismaClient } from "@prisma/client"; // Singleton PrismaClient untuk pengembangan const globalForPrisma = globalThis as unknown as { __prisma__: PrismaClient | undefined; }; -export const prisma = globalForPrisma.__prisma__ ?? new PrismaClient({ - // log: process.env.NODE_ENV === 'development' ? ['query', 'info', 'warn', 'error'] : [], -}); +export const prisma = + globalForPrisma.__prisma__ ?? + new PrismaClient({ + // log: process.env.NODE_ENV === 'development' ? ['query', 'info', 'warn', 'error'] : [], + }); // Gunakan PrismaClient yang sama jika sudah ada -if (process.env.NODE_ENV !== 'production') { +if (process.env.NODE_ENV !== "production") { if (!globalForPrisma.__prisma__) { - console.log('PrismaClient initialized in development mode'); + console.log("PrismaClient initialized in development mode"); } globalForPrisma.__prisma__ = prisma; } -process.on('SIGINT', async () => { - console.log('Disconnecting PrismaClient...'); - await prisma.$disconnect(); +process.on("SIGINT", async () => { + console.log("Disconnecting PrismaClient..."); + await prisma.$disconnect();3 process.exit(0); }); -// console.log('==> Test prisma'); +console.log("==> Test prisma"); export default prisma; diff --git a/src/app_modules/_global/button/comp_button_upload_photo.tsx b/src/app_modules/_global/button/comp_button_upload_photo.tsx index 8e56d342..378e7126 100644 --- a/src/app_modules/_global/button/comp_button_upload_photo.tsx +++ b/src/app_modules/_global/button/comp_button_upload_photo.tsx @@ -6,6 +6,7 @@ import { MainColor } from "../color"; import { MAX_SIZE } from "../lib"; import { PemberitahuanMaksimalFile } from "../lib/max_size"; import { ComponentGlobal_NotifikasiPeringatan } from "../notif_global"; +import { clientLogger } from "@/util/clientLogger"; export function ComponentGlobal_ButtonUploadFileImage({ onSetFile, @@ -30,7 +31,7 @@ export function ComponentGlobal_ButtonUploadFileImage({ onSetImage(buffer); } } catch (error) { - console.log(error); + clientLogger.error("Upload error:", error); } }} accept="image/png,image/jpeg" diff --git a/src/app_modules/auth/register/view.tsx b/src/app_modules/auth/register/view.tsx index 79d4b59a..8e6f84b3 100644 --- a/src/app_modules/auth/register/view.tsx +++ b/src/app_modules/auth/register/view.tsx @@ -65,13 +65,13 @@ export default function Register() { const result = await res.json(); if (res.status === 200) { - localStorage.removeItem("hipmi_auth_code_id"); ComponentGlobal_NotifikasiBerhasil(result.message); - router.push("/dev/home", { scroll: false }); - + localStorage.removeItem("hipmi_auth_code_id"); await auth_funDeleteAktivasiKodeOtpByNomor({ nomor: data.nomor, }); + router.push("/dev/home", { scroll: false }); + return; } if (res.status === 400) { diff --git a/src/app_modules/auth/validasi/view.tsx b/src/app_modules/auth/validasi/view.tsx index d0b39c01..83894734 100644 --- a/src/app_modules/auth/validasi/view.tsx +++ b/src/app_modules/auth/validasi/view.tsx @@ -59,11 +59,7 @@ export default function Validasi() { } }, [triggerOtp]); - async function onCheckAuthCode({ - kodeId, - }: { - kodeId: string; - }) { + async function onCheckAuthCode({ kodeId }: { kodeId: string }) { const res = await fetch(`/api/auth/check?id=${kodeId}`); const result = await res.json(); @@ -97,37 +93,40 @@ export default function Validasi() { const result = await res.json(); - if (res.status === 200) { + if (res.status === 200 && result.roleId == "1") { + ComponentGlobal_NotifikasiBerhasil(result.message); localStorage.removeItem("hipmi_auth_code_id"); - - if (result.roleId === "1") { - ComponentGlobal_NotifikasiBerhasil(result.message); - router.push(RouterHome.main_home, { scroll: false }); - // if (result.active === true) { - // } else { - // ComponentGlobal_NotifikasiBerhasil(result.message); - // router.push("/waiting-room", { scroll: false }); - // } - } else { - ComponentGlobal_NotifikasiBerhasil("Admin Logged in"); - router.push(RouterAdminDashboard.splash_admin, { scroll: false }); - } - await auth_funDeleteAktivasiKodeOtpByNomor({ nomor: data.nomor, }); + router.push(RouterHome.main_home, { scroll: false }); + return; + } + + if (res.status === 200 && result.roleId != "1") { + ComponentGlobal_NotifikasiBerhasil("Admin Logged in"); + localStorage.removeItem("hipmi_auth_code_id"); + await auth_funDeleteAktivasiKodeOtpByNomor({ + nomor: data.nomor, + }); + router.push(RouterAdminDashboard.splash_admin, { scroll: false }); + return; } if (res.status === 404) { - ComponentGlobal_NotifikasiBerhasil(result.message); router.push("/register", { scroll: false }); + ComponentGlobal_NotifikasiBerhasil(result.message); + return; } if (res.status === 400) { ComponentGlobal_NotifikasiPeringatan(result.message); + return; } } catch (error) { console.error(error); + } finally { + setLoading(false); } } diff --git a/src/app_modules/katalog/profile/_component/button/comp_button_update_background_profile.tsx b/src/app_modules/katalog/profile/_component/button/comp_button_update_background_profile.tsx index 92f369cf..bb947e84 100644 --- a/src/app_modules/katalog/profile/_component/button/comp_button_update_background_profile.tsx +++ b/src/app_modules/katalog/profile/_component/button/comp_button_update_background_profile.tsx @@ -1,47 +1,71 @@ +import { DIRECTORY_ID } from "@/app/lib"; import { MainColor } from "@/app_modules/_global/color"; +import { + funGlobal_DeleteFileById, + funGlobal_UploadToStorage, +} from "@/app_modules/_global/fun"; import { ComponentGlobal_NotifikasiBerhasil, ComponentGlobal_NotifikasiGagal, ComponentGlobal_NotifikasiPeringatan, } from "@/app_modules/_global/notif_global"; +import { clientLogger } from "@/util/clientLogger"; import { Box, Button } from "@mantine/core"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { profile_funUpdateBackground } from "../../fun/update/fun_update_background"; -import { DIRECTORY_ID } from "@/app/lib"; -import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; export function Profile_ComponentButtonUpdateBackgroundProfile({ - profileId, file, + profileId, + fileId, }: { - profileId: string; file: File; + profileId: string; + fileId: string; }) { const router = useRouter(); const [loading, setLoading] = useState(false); async function onUpdate() { - setLoading(true); - const uploadFile = await funGlobal_UploadToStorage({ - file: file, - dirId: DIRECTORY_ID.profile_background, - }); - if (!uploadFile.success) { - setLoading(false); - return ComponentGlobal_NotifikasiPeringatan("Gagal upload foto profile"); - } - - const res = await profile_funUpdateBackground({ - profileId: profileId, - fileId: uploadFile.data.id, - }); - if (res.status === 200) { + try { setLoading(true); - ComponentGlobal_NotifikasiBerhasil(res.message); - router.back(); - } else { + const deletePhoto = await funGlobal_DeleteFileById({ + fileId: fileId, + dirId: DIRECTORY_ID.profile_background, + }); + + if (!deletePhoto.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal update background"); + return; + } + + const uploadFile = await funGlobal_UploadToStorage({ + file: file, + dirId: DIRECTORY_ID.profile_background, + }); + + if (!uploadFile.success) { + setLoading(false); + return ComponentGlobal_NotifikasiPeringatan( + "Gagal upload background" + ); + } + + const res = await profile_funUpdateBackground({ + profileId: profileId, + fileId: uploadFile.data.id, + }); + + if (res.status === 200) { + ComponentGlobal_NotifikasiBerhasil(res.message); + router.back(); + } else { + ComponentGlobal_NotifikasiGagal(res.message); + } + } catch (error) { + clientLogger.error("Error upload background", error); + } finally { setLoading(false); - ComponentGlobal_NotifikasiGagal(res.message); } } diff --git a/src/app_modules/katalog/profile/_component/button/comp_button_update_photo_profile.tsx b/src/app_modules/katalog/profile/_component/button/comp_button_update_photo_profile.tsx index ab09104b..ac721d40 100644 --- a/src/app_modules/katalog/profile/_component/button/comp_button_update_photo_profile.tsx +++ b/src/app_modules/katalog/profile/_component/button/comp_button_update_photo_profile.tsx @@ -10,40 +10,62 @@ import { Box, Button, Center } from "@mantine/core"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { profile_funUpdatePhoto } from "../../fun"; -import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; +import { + funGlobal_DeleteFileById, + funGlobal_UploadToStorage, +} from "@/app_modules/_global/fun"; import { DIRECTORY_ID } from "@/app/lib"; +import { clientLogger } from "@/util/clientLogger"; export function Profile_ComponentButtonUpdatePhotoProfile({ file, profileId, + fileId, }: { file: File; profileId: string; + fileId: string; }) { const router = useRouter(); const [isLoading, setLoading] = useState(false); async function onUpdate() { - setLoading(true); - const uploadPhoto = await funGlobal_UploadToStorage({ - file: file, - dirId: DIRECTORY_ID.profile_foto, - }); - if (!uploadPhoto.success) { - setLoading(false); - return ComponentGlobal_NotifikasiPeringatan("Gagal upload foto profile"); - } - - const res = await profile_funUpdatePhoto({ - fileId: uploadPhoto.data.id, - profileId: profileId, - }); - if (res.status === 200) { + try { setLoading(true); - ComponentGlobal_NotifikasiBerhasil(res.message); - router.back(); - } else { + const deletePhoto = await funGlobal_DeleteFileById({ + fileId: fileId, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (!deletePhoto.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal update foto profile"); + return; + } + + const uploadPhoto = await funGlobal_UploadToStorage({ + file: file, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (!uploadPhoto.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal upload foto profile"); + return; + } + + const res = await profile_funUpdatePhoto({ + fileId: uploadPhoto.data.id, + profileId: profileId, + }); + + if (res.status === 200) { + ComponentGlobal_NotifikasiBerhasil(res.message); + router.back(); + } else { + ComponentGlobal_NotifikasiGagal(res.message); + } + } catch (error) { + clientLogger.error("Error update photo profile", error); + } finally { setLoading(false); - ComponentGlobal_NotifikasiGagal(res.message); } } return ( diff --git a/src/app_modules/katalog/profile/upload/foto_background/index.tsx b/src/app_modules/katalog/profile/upload/foto_background/index.tsx index 559b003c..4772c7ed 100644 --- a/src/app_modules/katalog/profile/upload/foto_background/index.tsx +++ b/src/app_modules/katalog/profile/upload/foto_background/index.tsx @@ -48,6 +48,7 @@ export default function Profile_UpdateFotoBackground({ diff --git a/src/app_modules/katalog/profile/upload/foto_profile/index.tsx b/src/app_modules/katalog/profile/upload/foto_profile/index.tsx index 4f1a960e..7eec7bdf 100644 --- a/src/app_modules/katalog/profile/upload/foto_profile/index.tsx +++ b/src/app_modules/katalog/profile/upload/foto_profile/index.tsx @@ -44,6 +44,7 @@ export default function UploadFotoProfile({
From c08d72539b1084a5318c1217bb37c891e0a93851 Mon Sep 17 00:00:00 2001 From: amel Date: Tue, 17 Dec 2024 11:56:58 +0800 Subject: [PATCH 15/23] upd : donasi Deskripsi: - update api list donasi galang data semua status - fix code more data No Issues --- src/app/api/new/donasi/route.ts | 25 +++++- .../dev/donasi/main/galang_dana/[id]/page.tsx | 24 +++--- .../component/card_view/card_publish_new.tsx | 2 - .../component/card_view/card_status_new.tsx | 53 ++++++++++++ src/app_modules/donasi/lib/type_donasi.ts | 1 + src/app_modules/donasi/main/beranda_new.tsx | 2 +- .../galang_dana/donasi_galang_dana_new.tsx | 80 +++++++++++++++++++ .../main/galang_dana/ui_galang_dana_new.tsx | 33 +++++--- .../main/portofolio/view_portofolio_new.tsx | 2 +- 9 files changed, 191 insertions(+), 31 deletions(-) create mode 100644 src/app_modules/donasi/component/card_view/card_status_new.tsx create mode 100644 src/app_modules/donasi/main/galang_dana/donasi_galang_dana_new.tsx diff --git a/src/app/api/new/donasi/route.ts b/src/app/api/new/donasi/route.ts index 2782e7de..22d9449d 100644 --- a/src/app/api/new/donasi/route.ts +++ b/src/app/api/new/donasi/route.ts @@ -46,23 +46,44 @@ export async function GET(request: Request) { ..._.omit(v, ["DonasiMaster_Durasi"]), nameDonasiDurasi: v.DonasiMaster_Durasi.name })) - } else { + } else if (kategori == "galang-dana") { const userLoginId = await funGetUserIdByToken() if (userLoginId == null) { return NextResponse.json({ success: false, message: "Gagal mendapatkan data, user id tidak ada" }, { status: 500 }); } - dataFix = await prisma.donasi.findMany({ + const data = await prisma.donasi.findMany({ take: 5, skip: dataSkip, where: { authorId: userLoginId, donasiMaster_StatusDonasiId: status, + active: true, + }, + select: { + id: true, + title: true, + imagesId: true, + target: true, + progres: true, + publishTime: true, + DonasiMaster_Durasi: { + select: { + name: true + } + }, + terkumpul: true, + imageId: true, }, orderBy: { updatedAt: "desc", }, }); + + dataFix = data.map((v: any) => ({ + ..._.omit(v, ["DonasiMaster_Durasi"]), + nameDonasiDurasi: v.DonasiMaster_Durasi.name + })) } return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); diff --git a/src/app/dev/donasi/main/galang_dana/[id]/page.tsx b/src/app/dev/donasi/main/galang_dana/[id]/page.tsx index b4ff467c..c3cfa869 100644 --- a/src/app/dev/donasi/main/galang_dana/[id]/page.tsx +++ b/src/app/dev/donasi/main/galang_dana/[id]/page.tsx @@ -1,27 +1,23 @@ -import { GalangDanaDonasiNew, PostingDonasi } from "@/app_modules/donasi"; -import { donasi_funGetAllStatusById } from "@/app_modules/donasi/fun"; -import { donasi_funMasterStatusDonasi } from "@/app_modules/donasi/fun/master/get_status_donasi"; +import { GalangDanaDonasiNew } from "@/app_modules/donasi"; export default async function Page({ params }: { params: { id: string } }) { - const statusId = params.id; + // const statusId = params.id; - const listStatus = await donasi_funMasterStatusDonasi(); - const dataDonasi = await donasi_funGetAllStatusById({ - page: 1, - statusId: statusId, - }); + // const listStatus = await donasi_funMasterStatusDonasi(); + // const dataDonasi = await donasi_funGetAllStatusById({ + // page: 1, + // statusId: statusId, + // }); return ( <> - + /> */} - - {/* NANTI AJA LANJUT -- KEJAR TAYANG :) */} - {/* */} + ); } diff --git a/src/app_modules/donasi/component/card_view/card_publish_new.tsx b/src/app_modules/donasi/component/card_view/card_publish_new.tsx index ab84a3a2..885fa359 100644 --- a/src/app_modules/donasi/component/card_view/card_publish_new.tsx +++ b/src/app_modules/donasi/component/card_view/card_publish_new.tsx @@ -13,8 +13,6 @@ export default function ComponentDonasi_CardPublishNew({ data }: { data: any; }) <> { - // DELSOON - console.log(RouterDonasi.detail_main + `${data.id}`) router.push(RouterDonasi.detail_main + `${data.id}`) }} > diff --git a/src/app_modules/donasi/component/card_view/card_status_new.tsx b/src/app_modules/donasi/component/card_view/card_status_new.tsx new file mode 100644 index 00000000..0d2d888e --- /dev/null +++ b/src/app_modules/donasi/component/card_view/card_status_new.tsx @@ -0,0 +1,53 @@ +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { ComponentGlobal_CardStyles, ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; +import { Grid, Stack, Text } from "@mantine/core"; +import { useParams, useRouter } from "next/navigation"; +import { IDataAllDonasi } from "../../lib/type_donasi"; + +export function ComponentDonasi_CardStatusNew({ data }: { data: IDataAllDonasi; }) { + const router = useRouter(); + const param = useParams<{ id: string }>(); + + function goToDetail() { + if (param.id == "2") { + router.push(RouterDonasi.detail_review + `${data.id}`); + } else if (param.id == "3") { + router.push(RouterDonasi.detail_draft + `${data.id}`); + } else if (param.id == "4") { + router.push(RouterDonasi.detail_reject + `${data.id}`); + } + } + + return ( + <> + { goToDetail() }} > + + + + + + + + + {data.title} + + + Target Dana + + Rp.{" "} + {new Intl.NumberFormat("id-ID", { + maximumFractionDigits: 10, + }).format(+data.target)} + + + + + + + + + ); +} diff --git a/src/app_modules/donasi/lib/type_donasi.ts b/src/app_modules/donasi/lib/type_donasi.ts index fabb23d5..2931b59f 100644 --- a/src/app_modules/donasi/lib/type_donasi.ts +++ b/src/app_modules/donasi/lib/type_donasi.ts @@ -5,5 +5,6 @@ export interface IDataAllDonasi { publishTime: Date progres: string terkumpul: string + target: string nameDonasiDurasi: string } \ No newline at end of file diff --git a/src/app_modules/donasi/main/beranda_new.tsx b/src/app_modules/donasi/main/beranda_new.tsx index f6d1b66c..a8c4dde1 100644 --- a/src/app_modules/donasi/main/beranda_new.tsx +++ b/src/app_modules/donasi/main/beranda_new.tsx @@ -117,7 +117,7 @@ export default function MainDonasiNew() { setActivePage((val) => val + 1); - return loadData; + return loadData.data; }} > {(item) => ( diff --git a/src/app_modules/donasi/main/galang_dana/donasi_galang_dana_new.tsx b/src/app_modules/donasi/main/galang_dana/donasi_galang_dana_new.tsx new file mode 100644 index 00000000..d4d81c42 --- /dev/null +++ b/src/app_modules/donasi/main/galang_dana/donasi_galang_dana_new.tsx @@ -0,0 +1,80 @@ +"use client"; +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; +import { Box, Center } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import ComponentDonasi_CardPublishNew from "../../component/card_view/card_publish_new"; +import SkeletonDonasi from "../../component/skeleton_donasi"; +import { apiGetAllDonasi } from "../../lib/api_donasi"; +import { IDataAllDonasi } from "../../lib/type_donasi"; +import { ComponentDonasi_CardStatusNew } from "../../component/card_view/card_status_new"; + +export default function Donasi_ViewGalangDanaNew() { + const param = useParams<{ id: string }>();; + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true) + + async function getDataGalangDana() { + try { + setLoading(true) + const response = await apiGetAllDonasi(`?cat=galang-dana&status=${param.id}&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataGalangDana() + }, []); + + return ( + <> + { + loading ? + : + _.isEmpty(data) ? ( + + ) : ( + + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllDonasi(`?cat=galang-dana&status=${param.id}&page=${pageNew}`) + setActivePage((val) => val + 1); + + return loadData.data; + }} + > + { + param.id == "1" ? + (item) => () + : + (item) => () + + } +
+
+ ) + } + + ); +} diff --git a/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx b/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx index dea0613a..1aa553df 100644 --- a/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx +++ b/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx @@ -1,13 +1,31 @@ "use client"; import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; import { AccentColor, MainColor, } from "@/app_modules/_global/color/color_pallet"; -import { MODEL_NEW_DEFAULT_MASTER } from "@/app_modules/model_global/interface"; import { Stack, Tabs } from "@mantine/core"; import { useParams, useRouter } from "next/navigation"; +import Donasi_ViewGalangDanaNew from "./donasi_galang_dana_new"; -export default function GalangDanaDonasiNew({listStatus,}: {listStatus: MODEL_NEW_DEFAULT_MASTER[]; }) { +export default function GalangDanaDonasiNew() { const router = useRouter(); const param = useParams<{ id: string }>(); + const status = [ + { + id: "1", + name: "Publish" + }, + { + id: "2", + name: "Review" + }, + { + id: "3", + name: "Draft" + }, + { + id: "4", + name: "Reject" + } + ] async function onChangeStatus({ statusId }: { statusId: string }) { router.replace(RouterDonasi.status_galang_dana({ id: statusId })); @@ -34,7 +52,7 @@ export default function GalangDanaDonasiNew({listStatus,}: {listStatus: MODEL_NE > - {listStatus.map((e, i) => ( + {status.map((e, i) => ( ))} - - {/* {param.id == "1" && } - - {param.id == "2" && } - - {param.id == "3" && } - - {param.id == "4" && } */} + diff --git a/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx b/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx index 417c0431..5c0a76c0 100644 --- a/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx +++ b/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx @@ -61,7 +61,7 @@ export function Investasi_ViewPortofolioNew() { const loadData = await apiGetAllInvestasi(`?cat=portofolio&status=${param.id}&page=${pageNew}`) setActivePage((val) => val + 1); - return loadData; + return loadData.data; }} > { From 1caa6d55631e428d53ae1eadff70a7eef57a5b38 Mon Sep 17 00:00:00 2001 From: amel Date: Tue, 17 Dec 2024 17:12:32 +0800 Subject: [PATCH 16/23] upd: donasi Deskripsi: - api donasi saya No Issues --- src/app/api/new/donasi/invoice/route.ts | 77 +++++++++++++++++ src/app/dev/donasi/main/donasi_saya/page.tsx | 12 ++- .../component/card_view/card_invoice_new.tsx | 83 +++++++++++++++++++ .../donasi/component/skeleton_donasi.tsx | 2 +- .../donasi/component/skeleton_donasi_saya.tsx | 32 +++++++ src/app_modules/donasi/lib/api_donasi.ts | 5 ++ src/app_modules/donasi/lib/type_donasi.ts | 13 +++ .../donasi/main/donasi_saya_new.tsx | 72 ++++++++++++++++ 8 files changed, 291 insertions(+), 5 deletions(-) create mode 100644 src/app/api/new/donasi/invoice/route.ts create mode 100644 src/app_modules/donasi/component/card_view/card_invoice_new.tsx create mode 100644 src/app_modules/donasi/component/skeleton_donasi_saya.tsx create mode 100644 src/app_modules/donasi/main/donasi_saya_new.tsx diff --git a/src/app/api/new/donasi/invoice/route.ts b/src/app/api/new/donasi/invoice/route.ts new file mode 100644 index 00000000..4585a9b9 --- /dev/null +++ b/src/app/api/new/donasi/invoice/route.ts @@ -0,0 +1,77 @@ +import { prisma } from "@/app/lib"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import _ from "lodash"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + + + +// GET ALL DATA DONASI SAYA (INVOICE) +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url) + const page = searchParams.get("page") + const dataSkip = Number(page) * 5 - 5; + + + const userLoginId = await funGetUserIdByToken() + if (userLoginId == null) { + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, user id tidak ada" }, { status: 500 }); + } + + const data = await prisma.donasi_Invoice.findMany({ + take: 5, + skip: dataSkip, + orderBy: { + createdAt: "desc", + }, + where: { + authorId: userLoginId, + }, + select: { + id: true, + nominal: true, + donasiMaster_StatusInvoiceId: true, + DonasiMaster_StatusInvoice: { + select: { + name: true + } + }, + Donasi: { + select: { + id: true, + title: true, + publishTime: true, + progres: true, + imageId: true, + DonasiMaster_Durasi: { + select: { + name: true + } + }, + }, + }, + }, + }); + + const dataFix = data.map((v: any) => ({ + ..._.omit(v, ["DonasiMaster_StatusInvoice", "Donasi"]), + nameStatusInvoice: v.DonasiMaster_StatusInvoice.name, + donasiId: v.Donasi.id, + title: v.Donasi.title, + publishTime: v.Donasi.publishTime, + progres: v.Donasi.progres, + imageId: v.Donasi.imageId, + durasiDonasi: v.Donasi.DonasiMaster_Durasi.name + })) + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti ", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/dev/donasi/main/donasi_saya/page.tsx b/src/app/dev/donasi/main/donasi_saya/page.tsx index bbb25e4a..a04c7954 100644 --- a/src/app/dev/donasi/main/donasi_saya/page.tsx +++ b/src/app/dev/donasi/main/donasi_saya/page.tsx @@ -1,8 +1,12 @@ -import { DonasiSayaDonasi } from "@/app_modules/donasi"; -import { donasi_funGetAllInvoiceByAuthorId } from "@/app_modules/donasi/fun/get/get_all_invoice_by_author_id"; +import DonasiSayaNew from "@/app_modules/donasi/main/donasi_saya_new"; export default async function Page() { - const listInvoice = await donasi_funGetAllInvoiceByAuthorId({ page: 1 }); + // const listInvoice = await donasi_funGetAllInvoiceByAuthorId({ page: 1 }); - return ; + return ( + <> + {/* ; */} + + + ) } diff --git a/src/app_modules/donasi/component/card_view/card_invoice_new.tsx b/src/app_modules/donasi/component/card_view/card_invoice_new.tsx new file mode 100644 index 00000000..c5712648 --- /dev/null +++ b/src/app_modules/donasi/component/card_view/card_invoice_new.tsx @@ -0,0 +1,83 @@ +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { AccentColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; +import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; +import { Badge, Card, Grid, Group, Progress, Stack, Text } from "@mantine/core"; +import { useRouter } from "next/navigation"; +import { IDataAllDonasiSaya } from "../../lib/type_donasi"; +import ComponentDonasi_TampilanHitungMundur from "../tampilan_hitung_mundur"; +import TampilanRupiahDonasi from "../tampilan_rupiah"; + +export function ComponentDonasi_CardInvoiceNew({ data, }: { data: IDataAllDonasiSaya; }) { + const router = useRouter(); + + async function onCekInvoice() { + if (data.donasiMaster_StatusInvoiceId === "1") { + return router.push(RouterDonasi.detail_donasi_saya + `${data?.id}`); + } else { + if (data.donasiMaster_StatusInvoiceId === "2") { + return router.push(RouterDonasi.proses_transaksi + `${data?.id}`); + } else { + if (data.donasiMaster_StatusInvoiceId === "3") { + return router.push(RouterDonasi.invoice + `${data?.id}`); + } else { + ComponentGlobal_NotifikasiGagal("Gagal Melihat Invoice"); + } + } + } + } + + return ( + <> + onCekInvoice()} + > + + + + + + + {data.title} + + + + + + + Donasi Saya + + + + + + + {data.nameStatusInvoice} + + + + + + + + + + + ); +} diff --git a/src/app_modules/donasi/component/skeleton_donasi.tsx b/src/app_modules/donasi/component/skeleton_donasi.tsx index 7c27f924..7f461e64 100644 --- a/src/app_modules/donasi/component/skeleton_donasi.tsx +++ b/src/app_modules/donasi/component/skeleton_donasi.tsx @@ -1,5 +1,5 @@ import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; -import { Box, Grid, Group, Skeleton, Stack } from "@mantine/core"; +import { Box, Grid, Skeleton } from "@mantine/core"; export default function SkeletonDonasi() { return <> diff --git a/src/app_modules/donasi/component/skeleton_donasi_saya.tsx b/src/app_modules/donasi/component/skeleton_donasi_saya.tsx new file mode 100644 index 00000000..1b3b3a2b --- /dev/null +++ b/src/app_modules/donasi/component/skeleton_donasi_saya.tsx @@ -0,0 +1,32 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Box, Grid, Skeleton } from "@mantine/core"; + +export default function SkeletonDonasiSaya() { + return <> + + {[...Array(3)].map((_, index) => ( + + + + + {[...Array(4)].map((_, index) => ( + + + + + + + + ))} + + + + + + + + + ))} + + ; +} \ No newline at end of file diff --git a/src/app_modules/donasi/lib/api_donasi.ts b/src/app_modules/donasi/lib/api_donasi.ts index 04f661f3..2f7e719e 100644 --- a/src/app_modules/donasi/lib/api_donasi.ts +++ b/src/app_modules/donasi/lib/api_donasi.ts @@ -6,4 +6,9 @@ export const apiGetAllDonasi = async (path?: string) => { export const apiGetMasterDonasi = async (path?: string) => { const response = await fetch(`/api/new/donasi/master${(path) ? path : ''}`) return await response.json().catch(() => null) +} + +export const apiGetAllDonasiSaya = async (path?: string) => { + const response = await fetch(`/api/new/donasi/invoice${(path) ? path : ''}`) + return await response.json().catch(() => null) } \ No newline at end of file diff --git a/src/app_modules/donasi/lib/type_donasi.ts b/src/app_modules/donasi/lib/type_donasi.ts index 2931b59f..912d067f 100644 --- a/src/app_modules/donasi/lib/type_donasi.ts +++ b/src/app_modules/donasi/lib/type_donasi.ts @@ -7,4 +7,17 @@ export interface IDataAllDonasi { terkumpul: string target: string nameDonasiDurasi: string +} + +export interface IDataAllDonasiSaya { + id: string + nominal: string + donasiMaster_StatusInvoiceId: string + nameStatusInvoice: string + donasiId: string + title: string + publishTime: Date + progres: string + imageId: string + durasiDonasi: string } \ No newline at end of file diff --git a/src/app_modules/donasi/main/donasi_saya_new.tsx b/src/app_modules/donasi/main/donasi_saya_new.tsx new file mode 100644 index 00000000..0ecf12b1 --- /dev/null +++ b/src/app_modules/donasi/main/donasi_saya_new.tsx @@ -0,0 +1,72 @@ +"use client"; +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; +import { Box, Center } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useState } from "react"; +import { ComponentDonasi_CardInvoiceNew } from "../component/card_view/card_invoice_new"; +import SkeletonDonasiSaya from "../component/skeleton_donasi_saya"; +import { apiGetAllDonasiSaya } from "../lib/api_donasi"; +import { IDataAllDonasiSaya } from "../lib/type_donasi"; + +export default function DonasiSayaNew() { + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true) + + async function getDataDonasiSaya() { + try { + setLoading(true) + const response = await apiGetAllDonasiSaya(`?page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataDonasiSaya() + }, []); + + return ( + <> + + { + loading ? + + : + _.isEmpty(data) ? ( + + ) : ( + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllDonasiSaya(`?page=${pageNew}`) + setActivePage((val) => val + 1); + + return loadData.data; + }} + > + {(item) => } +
+ ) + } +
+ + ); +} \ No newline at end of file From 2fa3f22f9c907de560f4b4e865a6964582a6df16 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 17 Dec 2024 22:40:38 +0800 Subject: [PATCH 17/23] Fix notifikasi Deskripsi: - Ganti get notifikasi dengan API --- .../notifikasi/get-all-by-category/route.ts | 63 ++++++++++++++++ src/app/dev/notifikasi/event/page.tsx | 8 +-- src/app/dev/notifikasi/semua/page.tsx | 8 +-- .../api_user_router/route_api_notifikasi.ts | 11 +++ src/app_modules/home/view_home_new.tsx | 4 ++ .../notifikasi/_ui/ui_all_notifikasi.tsx | 72 +++++++++++-------- .../notifikasi/_ui/ui_event_notifikasi.tsx | 51 +++++++------ .../_ui/ui_new_layout_notifikasi.tsx | 17 ++--- .../notifikasi/component/card_view.tsx | 17 ++--- src/app_modules/notifikasi/component/index.ts | 2 + .../notifikasi/component/skeleton_view.tsx | 29 ++++++++ src/app_modules/notifikasi/model/interface.ts | 12 +++- src/middleware.ts | 3 +- 13 files changed, 208 insertions(+), 89 deletions(-) create mode 100644 src/app/api/notifikasi/get-all-by-category/route.ts create mode 100644 src/app/lib/api_user_router/route_api_notifikasi.ts create mode 100644 src/app_modules/notifikasi/component/skeleton_view.tsx diff --git a/src/app/api/notifikasi/get-all-by-category/route.ts b/src/app/api/notifikasi/get-all-by-category/route.ts new file mode 100644 index 00000000..b13ce671 --- /dev/null +++ b/src/app/api/notifikasi/get-all-by-category/route.ts @@ -0,0 +1,63 @@ +import { prisma } from "@/app/lib"; +import { newFunGetUserId } from "@/app/lib/new_fun_user_id"; +import { ICategoryapp } from "@/app_modules/notifikasi/model/interface"; +import backendLogger from "@/util/backendLogger"; +import _ from "lodash"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + +export async function GET(request: Request) { + if (request.method === "GET") { + try { + const { searchParams } = new URL(request.url); + const category = searchParams.get("category") as ICategoryapp; + const page = searchParams.get("page"); + + const userLoginId = await newFunGetUserId(); + const fixPage = _.toNumber(page); + const takeData = 10; + const skipData = fixPage * takeData - takeData; + + if (category === "Semua") { + const data = await prisma.notifikasi.findMany({ + take: takeData, + skip: skipData, + orderBy: [ + { + isRead: "asc", + }, + { createdAt: "desc" }, + ], + where: { + userId: userLoginId, + userRoleId: "1", + }, + }); + + return NextResponse.json({ success: true, data }); + } + + const allData = await prisma.notifikasi.findMany({ + take: takeData, + skip: skipData, + orderBy: [ + { + isRead: "asc", + }, + { createdAt: "desc" }, + ], + where: { + userId: userLoginId, + userRoleId: "1", + kategoriApp: _.upperCase(category), + }, + }); + + return NextResponse.json({ success: true, data: allData }); + } catch (error) { + backendLogger.error("Error get data notifikasi: " + error); + } + } else { + return NextResponse.json({ success: false, message: "Method not allowed" }); + } +} diff --git a/src/app/dev/notifikasi/event/page.tsx b/src/app/dev/notifikasi/event/page.tsx index fa2ce235..3b7b8e34 100644 --- a/src/app/dev/notifikasi/event/page.tsx +++ b/src/app/dev/notifikasi/event/page.tsx @@ -1,15 +1,11 @@ import { Notifikasi_UiEvent } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Event", - }); + // return ( <> - + ); } diff --git a/src/app/dev/notifikasi/semua/page.tsx b/src/app/dev/notifikasi/semua/page.tsx index f9fad445..2f691b3c 100644 --- a/src/app/dev/notifikasi/semua/page.tsx +++ b/src/app/dev/notifikasi/semua/page.tsx @@ -1,15 +1,9 @@ import { Notifikasi_UiAll } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Semua", - }); - return ( <> - + ); } diff --git a/src/app/lib/api_user_router/route_api_notifikasi.ts b/src/app/lib/api_user_router/route_api_notifikasi.ts new file mode 100644 index 00000000..94027735 --- /dev/null +++ b/src/app/lib/api_user_router/route_api_notifikasi.ts @@ -0,0 +1,11 @@ +import { ICategoryapp } from "@/app_modules/notifikasi/model/interface"; + +export const API_RouteNotifikasi = { + get_all_by_category: ({ + category, + page, + }: { + category: ICategoryapp; + page: number; + }) => `/api/notifikasi/get-all-by-category?category=${category}&page=${page}`, +}; diff --git a/src/app_modules/home/view_home_new.tsx b/src/app_modules/home/view_home_new.tsx index de88ed02..59bca9fc 100644 --- a/src/app_modules/home/view_home_new.tsx +++ b/src/app_modules/home/view_home_new.tsx @@ -17,6 +17,7 @@ import BodyHome from "./component/body_home"; import FooterHome from "./component/footer_home"; import { apiGetDataHome } from "./fun/get/api_home"; import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; +import { gs_notifikasi_kategori_app } from "../notifikasi/lib"; export default function HomeViewNew({ countNotifikasi, @@ -27,6 +28,7 @@ export default function HomeViewNew({ const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); const [dataUser, setDataUser] = useState({}); + const [categoryPage, setCategoryPage] = useAtom(gs_notifikasi_kategori_app); const router = useRouter(); useShallowEffect(() => { @@ -98,12 +100,14 @@ export default function HomeViewNew({ ) { router.push(RouterProfile.create, { scroll: false }); } else { + setCategoryPage("Semua") router.push( RouterNotifikasi.categoryApp({ name: "semua" }), { scroll: false, } ); + } }} > diff --git a/src/app_modules/notifikasi/_ui/ui_all_notifikasi.tsx b/src/app_modules/notifikasi/_ui/ui_all_notifikasi.tsx index 02553505..7d3fa08f 100644 --- a/src/app_modules/notifikasi/_ui/ui_all_notifikasi.tsx +++ b/src/app_modules/notifikasi/_ui/ui_all_notifikasi.tsx @@ -2,46 +2,57 @@ import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; -import { Box, Center } from "@mantine/core"; +import { + Box, + Center, + Divider, + Grid, + Group, + Loader, + Skeleton, + Stack, +} from "@mantine/core"; import _ from "lodash"; import { ScrollOnly } from "next-scroll-loader"; import { useState } from "react"; -import { ComponentNotifiaksi_CardView } from "../component"; +import { + ComponentNotifiaksi_CardView, + Notifikasi_ComponentSkeletonView, +} from "../component"; import notifikasi_getByUserId from "../fun/get/get_notifiaksi_by_id"; -import { MODEL_NOTIFIKASI } from "../model/interface"; +import { ICategoryapp, MODEL_NOTIFIKASI } from "../model/interface"; import { useAtom } from "jotai"; import { gs_notifikasi_kategori_app } from "../lib"; import { useShallowEffect } from "@mantine/hooks"; +import { API_RouteNotifikasi } from "@/app/lib/api_user_router/route_api_notifikasi"; +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; -export default function Notifikasi_UiAll({ - listNotifikasi, -}: { - listNotifikasi: any[]; -}) { - const [data, setData] = useState(listNotifikasi); +export default function Notifikasi_UiAll() { + const [data, setData] = useState(null); const [activePage, setActivePage] = useState(1); const [categoryPage, setCategoryPage] = useAtom(gs_notifikasi_kategori_app); useShallowEffect(() => { - onLoadData({ - onSetData(val: any) { - setData(val); - }, - }); - }, [setData]); + onLoadData(); + }, []); - async function onLoadData({ onSetData }: { onSetData: any }) { - const loadData = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Semua", - }); - onSetData(loadData); + async function onLoadData() { + const loadData = await fetch( + API_RouteNotifikasi.get_all_by_category({ + category: categoryPage as any, + page: 1, + }) + ); + const data = await loadData.json().then((res) => res.data as any); + setData(data); } return ( <> - {_.isEmpty(data) ? ( + {_.isNull(data) ? ( + + ) : _.isEmpty(data) ? ( ) : ( )} data={data} - setData={setData} + setData={setData as any} moreData={async () => { - const loadData = await notifikasi_getByUserId({ - page: activePage + 1, - kategoriApp: categoryPage as any, - }); + const loadData = await fetch( + API_RouteNotifikasi.get_all_by_category({ + category: categoryPage as any, + page: activePage + 1, + }) + ); + const data = await loadData.json().then((res) => res.data as any); setActivePage((val) => val + 1); - return loadData; + return data; }} > {(item) => ( )} diff --git a/src/app_modules/notifikasi/_ui/ui_event_notifikasi.tsx b/src/app_modules/notifikasi/_ui/ui_event_notifikasi.tsx index d823a46e..b88c9cb7 100644 --- a/src/app_modules/notifikasi/_ui/ui_event_notifikasi.tsx +++ b/src/app_modules/notifikasi/_ui/ui_event_notifikasi.tsx @@ -7,37 +7,39 @@ import { useAtom } from "jotai"; import _ from "lodash"; import { ScrollOnly } from "next-scroll-loader"; import { useState } from "react"; -import { ComponentNotifiaksi_CardView } from "../component"; +import { + ComponentNotifiaksi_CardView, + Notifikasi_ComponentSkeletonView, +} from "../component"; import notifikasi_getByUserId from "../fun/get/get_notifiaksi_by_id"; import { gs_notifikasi_kategori_app } from "../lib"; import { MODEL_NOTIFIKASI } from "../model/interface"; import { useShallowEffect } from "@mantine/hooks"; +import { API_RouteNotifikasi } from "@/app/lib/api_user_router/route_api_notifikasi"; -export default function Notifikasi_UiEvent({ - listNotifikasi, -}: { - listNotifikasi: any[]; -}) { - const [data, setData] = useState(listNotifikasi); +export default function Notifikasi_UiEvent() { + const [data, setData] = useState(null); const [activePage, setActivePage] = useState(1); const [categoryPage, setCategoryPage] = useAtom(gs_notifikasi_kategori_app); useShallowEffect(() => { - onLoadData(setData); - }, [setData]); + onLoadData(); + }, []); - async function onLoadData(setData: any) { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Event", - }); - setData(listNotifikasi); + async function onLoadData() { + const loadData = await fetch( + API_RouteNotifikasi.get_all_by_category({ category: categoryPage as any, page: 1 }) + ); + const data = await loadData.json().then((res) => res.data as any); + setData(data); } return ( <> - {_.isEmpty(data) ? ( + {_.isNull(data) ? ( + + ) : _.isEmpty(data) ? ( ) : ( )} data={data} - setData={setData} + setData={setData as any} moreData={async () => { - const loadData = await notifikasi_getByUserId({ - page: activePage + 1, - kategoriApp: categoryPage as any, - }); + const loadData = await fetch( + API_RouteNotifikasi.get_all_by_category({ + category: categoryPage as any, + page: activePage + 1, + }) + ); + const data = await loadData.json().then((res) => res.data as any); setActivePage((val) => val + 1); - return loadData; + return data; }} > {(item) => ( )} diff --git a/src/app_modules/notifikasi/_ui/ui_new_layout_notifikasi.tsx b/src/app_modules/notifikasi/_ui/ui_new_layout_notifikasi.tsx index 064e9f55..81b16b89 100644 --- a/src/app_modules/notifikasi/_ui/ui_new_layout_notifikasi.tsx +++ b/src/app_modules/notifikasi/_ui/ui_new_layout_notifikasi.tsx @@ -3,12 +3,12 @@ import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi"; import { MainColor } from "@/app_modules/_global/color/color_pallet"; import { - BackgroundImage, - Box, - Button, - Container, - Flex, - rem, + BackgroundImage, + Box, + Button, + Container, + Flex, + rem, } from "@mantine/core"; import { useAtom } from "jotai"; import _ from "lodash"; @@ -108,9 +108,7 @@ function UIChildren({ style={{ transition: "0.3s", backgroundColor: - categoryPage === e.name - ? MainColor.yellow - : "GrayText", + categoryPage === e.name ? MainColor.yellow : "GrayText", }} onClick={() => { router.replace( @@ -120,7 +118,6 @@ function UIChildren({ } ); setCategoryPage(e.name); - // onLoadDataNotifikasi(e.name); }} > {e.name} diff --git a/src/app_modules/notifikasi/component/card_view.tsx b/src/app_modules/notifikasi/component/card_view.tsx index d3f2b07f..c6171e9a 100644 --- a/src/app_modules/notifikasi/component/card_view.tsx +++ b/src/app_modules/notifikasi/component/card_view.tsx @@ -13,13 +13,15 @@ import { IconCheck, IconChecks } from "@tabler/icons-react"; import { useAtom } from "jotai"; import { useRouter } from "next/navigation"; import { useState } from "react"; -import { MODEL_NOTIFIKASI } from "../model/interface"; +import { ICategoryapp, MODEL_NOTIFIKASI } from "../model/interface"; import { notifikasi_eventCheckStatus } from "./path/event"; import { notifikasi_jobCheckStatus } from "./path/job"; import { gs_vote_hotMenu } from "@/app_modules/vote/global_state"; import { notifikasi_votingCheckStatus } from "./path/voting"; import { redirectDonasiPage } from "./path/donasi"; import { gs_donasi_hot_menu } from "@/app_modules/donasi/global_state"; +import moment from "moment"; +import "moment/locale/id"; export function ComponentNotifiaksi_CardView({ data, @@ -28,7 +30,7 @@ export function ComponentNotifiaksi_CardView({ }: { data: MODEL_NOTIFIKASI; onLoadData: (val: any) => void; - categoryPage: any; + categoryPage: string; }) { const router = useRouter(); const [visible, setVisible] = useState(false); @@ -220,16 +222,7 @@ export function ComponentNotifiaksi_CardView({ - {new Intl.DateTimeFormat("id-ID", { - dateStyle: "long", - }).format(data?.createdAt)} - - - {", "} - {new Intl.DateTimeFormat("id-ID", { - timeStyle: "short", - }).format(data?.createdAt)} - + {moment(data.createdAt).format("LLL")} {data?.isRead ? ( diff --git a/src/app_modules/notifikasi/component/index.ts b/src/app_modules/notifikasi/component/index.ts index cf740b8c..9d4b1d74 100644 --- a/src/app_modules/notifikasi/component/index.ts +++ b/src/app_modules/notifikasi/component/index.ts @@ -1,5 +1,7 @@ import { ComponentNotifikasi_CardSkeleton } from "./card_skeleton"; import { ComponentNotifiaksi_CardView } from "./card_view"; +import Notifikasi_ComponentSkeletonView from "./skeleton_view"; export { ComponentNotifiaksi_CardView }; export { ComponentNotifikasi_CardSkeleton }; +export { Notifikasi_ComponentSkeletonView }; diff --git a/src/app_modules/notifikasi/component/skeleton_view.tsx b/src/app_modules/notifikasi/component/skeleton_view.tsx new file mode 100644 index 00000000..c5d8f302 --- /dev/null +++ b/src/app_modules/notifikasi/component/skeleton_view.tsx @@ -0,0 +1,29 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Stack, Group, Skeleton, Divider } from "@mantine/core"; + +export default function Notifikasi_ComponentSkeletonView() { + return ( + <> + {Array.from({ length: 10 }).map((_, i) => ( + + + + + + + + + + + + + + + + + + + ))} + + ); +} diff --git a/src/app_modules/notifikasi/model/interface.ts b/src/app_modules/notifikasi/model/interface.ts index 8697bfd0..fa526102 100644 --- a/src/app_modules/notifikasi/model/interface.ts +++ b/src/app_modules/notifikasi/model/interface.ts @@ -33,8 +33,18 @@ export interface MODEL_NOTIFIKASI { | "Gagal" | "Donatur Baru" | "Kabar Donasi" - | "Pencairan Dana" + | "Pencairan Dana"; Role: MODEL_NEW_DEFAULT_MASTER; userRoleId: String; } + +export type ICategoryapp = + | "Semua" + | "Event" + | "Job" + | "Voting" + | "Donasi" + | "Investasi" + | "Forum" + | "Collaboration"; diff --git a/src/middleware.ts b/src/middleware.ts index 6a72d076..2c3e8275 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -19,6 +19,7 @@ const middlewareConfig: MiddlewareConfig = { userPath: "/dev/home", publicRoutes: [ "/", + "/api/notifikasi/*", "/api/logs/*", "/api/image/*", "/api/job/*", @@ -35,7 +36,7 @@ const middlewareConfig: MiddlewareConfig = { "/auth/api/login", "/aset/global/main_background.png", "/aset/logo/logo-hipmi.png", - "/api/new/*" + "/api/new/*", ], encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, sessionKey: process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, From fe76a695323dc0632abc9c1465f1bbd324ec7fa4 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 17 Dec 2024 22:41:47 +0800 Subject: [PATCH 18/23] chore(release): 1.2.31 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56cbce3a..dc295cf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [1.2.31](https://github.com/bipproduction/hipmi/compare/v1.2.30...v1.2.31) (2024-12-17) + ## [1.2.30](https://github.com/bipproduction/hipmi/compare/v1.2.29...v1.2.30) (2024-12-16) ## [1.2.29](https://github.com/bipproduction/hipmi/compare/v1.2.28...v1.2.29) (2024-12-13) diff --git a/package.json b/package.json index 5091e4d2..5d5a2564 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.30", + "version": "1.2.31", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 8c9a885f4325d206eb0fe6eb544f85896542467a Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 17 Dec 2024 22:43:59 +0800 Subject: [PATCH 19/23] Fix version 1.2.31 --- src/app/lib/prisma.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/lib/prisma.ts b/src/app/lib/prisma.ts index 07ccb3e8..101fd93b 100644 --- a/src/app/lib/prisma.ts +++ b/src/app/lib/prisma.ts @@ -25,6 +25,6 @@ process.on("SIGINT", async () => { process.exit(0); }); -console.log("==> Test prisma"); +// console.log("==> Test prisma"); export default prisma; From c1931cf614179c15bea39f21aae35b6ff7f831fd Mon Sep 17 00:00:00 2001 From: amel Date: Wed, 18 Dec 2024 15:17:14 +0800 Subject: [PATCH 20/23] upd: investasi Deskripsi: - update api saham saya - update api transaksi investasi No Issues --- src/app/api/new/investasi/invoice/route.ts | 99 +++++++++++++++++++ .../dev/investasi/main/saham_saya/page.tsx | 8 +- src/app/dev/investasi/main/transaksi/page.tsx | 4 +- .../main/comp_card_daftar_transaksi_new.tsx | 94 ++++++++++++++++++ .../main/comp_card_saham_saya_new.tsx | 66 +++++++++++++ .../investasi/_lib/api_interface.ts | 5 + .../investasi/_lib/type_investasi.ts | 11 +++ .../_view/main/skeleton_saham_saya.tsx | 18 ++++ .../_view/main/view_saham_saya_new.tsx | 73 ++++++++++++++ .../_view/main/view_transaksi_new.tsx | 72 ++++++++++++++ 10 files changed, 445 insertions(+), 5 deletions(-) create mode 100644 src/app/api/new/investasi/invoice/route.ts create mode 100644 src/app_modules/investasi/_component/main/comp_card_daftar_transaksi_new.tsx create mode 100644 src/app_modules/investasi/_component/main/comp_card_saham_saya_new.tsx create mode 100644 src/app_modules/investasi/_view/main/skeleton_saham_saya.tsx create mode 100644 src/app_modules/investasi/_view/main/view_saham_saya_new.tsx create mode 100644 src/app_modules/investasi/_view/main/view_transaksi_new.tsx diff --git a/src/app/api/new/investasi/invoice/route.ts b/src/app/api/new/investasi/invoice/route.ts new file mode 100644 index 00000000..1f8b4380 --- /dev/null +++ b/src/app/api/new/investasi/invoice/route.ts @@ -0,0 +1,99 @@ +import { prisma } from "@/app/lib"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import _ from "lodash"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + + +// GET ALL DATA INVESTASI SAYA (INVOICE) +export async function GET(request: Request) { + try { + let dataFix + const { searchParams } = new URL(request.url) + const page = searchParams.get("page") + const dataSkip = Number(page) * 10 - 10; + const kategori = searchParams.get("cat") + + + const userLoginId = await funGetUserIdByToken() + if (userLoginId == null) { + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, user id tidak ada" }, { status: 500 }); + } + + if (kategori == "saham-saya") { + const data = await prisma.investasi_Invoice.findMany({ + take: 10, + skip: dataSkip, + orderBy: { + updatedAt: "desc", + }, + where: { + authorId: userLoginId, + statusInvoiceId: "1", + isActive: true, + }, + select: { + id: true, + nominal: true, + lembarTerbeli: true, + Investasi: { + select: { + title: true, + progress: true + } + } + } + }); + + dataFix = data.map((v: any) => ({ + ..._.omit(v, ["Investasi"]), + title: v.Investasi.title, + progress: v.Investasi.progress + })) + + } else if (kategori == "transaksi") { + const data = await prisma.investasi_Invoice.findMany({ + take: 10, + skip: dataSkip, + orderBy: { + updatedAt: "desc", + }, + where: { + authorId: userLoginId, + }, + select: { + id: true, + statusInvoiceId: true, + nominal: true, + createdAt: true, + StatusInvoice: { + select: { + name: true + } + }, + Investasi: { + select: { + title: true + } + } + } + }); + + + dataFix = data.map((v: any) => ({ + ..._.omit(v, ["Investasi", "StatusInvoice"]), + title: v.Investasi.title, + statusInvoice: v.StatusInvoice.name + })) + } + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti ", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/dev/investasi/main/saham_saya/page.tsx b/src/app/dev/investasi/main/saham_saya/page.tsx index fcf6570f..9b675e2c 100644 --- a/src/app/dev/investasi/main/saham_saya/page.tsx +++ b/src/app/dev/investasi/main/saham_saya/page.tsx @@ -1,13 +1,13 @@ -import { investasi_funGetSuccessTransactionById } from "@/app_modules/investasi/_fun"; -import { Investasi_UiSahamSaya } from "@/app_modules/investasi/_ui"; +import { Investasi_ViewSahamSayaNew } from "@/app_modules/investasi/_view/main/view_saham_saya_new"; export default async function Page() { - const dataSaham = await investasi_funGetSuccessTransactionById({ page: 1 }); + // const dataSaham = await investasi_funGetSuccessTransactionById({ page: 1 }); return ( <> {/* */} - + {/* */} + ); } diff --git a/src/app/dev/investasi/main/transaksi/page.tsx b/src/app/dev/investasi/main/transaksi/page.tsx index 78150c82..04b8850f 100644 --- a/src/app/dev/investasi/main/transaksi/page.tsx +++ b/src/app/dev/investasi/main/transaksi/page.tsx @@ -4,6 +4,7 @@ import getMaster_StatusTransaksiInvestasi from "@/app_modules/investasi/fun/mast import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; import { investasi_funGetTransaksiByUserId } from "@/app_modules/investasi/_fun"; import { Investasi_UiDaftarTransaksi } from "@/app_modules/investasi/_ui"; +import { Investasi_ViewDaftarTransaksiNew } from "@/app_modules/investasi/_view/main/view_transaksi_new"; export default async function Page() { const userLoginId = await funGetUserIdByToken(); @@ -22,7 +23,8 @@ export default async function Page() { statusTransaksi={statusTransaksi as any} listTransaksi={listTransaksi as any} /> */} - + {/* */} + ); } diff --git a/src/app_modules/investasi/_component/main/comp_card_daftar_transaksi_new.tsx b/src/app_modules/investasi/_component/main/comp_card_daftar_transaksi_new.tsx new file mode 100644 index 00000000..0ada112b --- /dev/null +++ b/src/app_modules/investasi/_component/main/comp_card_daftar_transaksi_new.tsx @@ -0,0 +1,94 @@ +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { AccentColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan"; +import { Card, Group, Stack, Text, Title } from "@mantine/core"; +import { useRouter } from "next/navigation"; +import { IDataSahamSaya } from "../../_lib/type_investasi"; + +export function Investasi_ComponentCardDaftarTransaksiNew({ data }: { data: IDataSahamSaya; }) { + const router = useRouter(); + + async function onClick({ invoiceId, statusInvoiceId, }: { invoiceId: string; statusInvoiceId: string; }) { + // Berhasil + if (statusInvoiceId === "1") { + return router.push(NEW_RouterInvestasi.transaksi_berhasil + invoiceId, { + scroll: false, + }); + } + + // Proses + if (statusInvoiceId === "2") { + return router.push(NEW_RouterInvestasi.proses_transaksi + invoiceId, { + scroll: false, + }); + } + + // Menunggu + if (statusInvoiceId === "3") { + return router.push(NEW_RouterInvestasi.invoice + invoiceId, { + scroll: false, + }); + } + + if (statusInvoiceId === "4") { + return router.push(NEW_RouterInvestasi.transaksi_gagal + invoiceId, { + scroll: false, + }); + } + ComponentGlobal_NotifikasiPeringatan("Status Belum Tersedia"); + } + + + + return ( + <> + + onClick({ + invoiceId: data.id, + statusInvoiceId: data.statusInvoiceId, + }) + } + > + + {data.title} + + Rp. + {new Intl.NumberFormat("id-ID", { + maximumFractionDigits: 10, + }).format(+data.nominal)} + + + + + + {new Intl.DateTimeFormat("id-ID", { dateStyle: "long" }).format(new Date(data.createdAt))} + + + + {data.statusInvoice} + + + + + ); +} diff --git a/src/app_modules/investasi/_component/main/comp_card_saham_saya_new.tsx b/src/app_modules/investasi/_component/main/comp_card_saham_saya_new.tsx new file mode 100644 index 00000000..bfaee9d9 --- /dev/null +++ b/src/app_modules/investasi/_component/main/comp_card_saham_saya_new.tsx @@ -0,0 +1,66 @@ +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { MainColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_TampilanAngkaRatusan, ComponentGlobal_TampilanRupiah } from "@/app_modules/_global/component"; +import { Box, Progress, SimpleGrid, Stack, Text } from "@mantine/core"; +import { useRouter } from "next/navigation"; +import { IDataSahamSaya } from "../../_lib/type_investasi"; +import { Investasi_ComponentStylesCard } from "../comp_card_border_and_background"; + +export function Investasi_ComponentSahamSayaNew({ data }: { data: IDataSahamSaya; }) { + const router = useRouter(); + + return ( + <> + { + router.push(NEW_RouterInvestasi.detail_saham + data?.id, { + scroll: false, + }); + }} + marginBottom={"15px"} + > + + + + + {data?.title} + + + + + + + + + + + + + ); +} diff --git a/src/app_modules/investasi/_lib/api_interface.ts b/src/app_modules/investasi/_lib/api_interface.ts index eb9aafda..a1475cff 100644 --- a/src/app_modules/investasi/_lib/api_interface.ts +++ b/src/app_modules/investasi/_lib/api_interface.ts @@ -11,4 +11,9 @@ export const apiGetOneInvestasiById = async (path: string) => { export const apiGetAllInvestasi = async (path?: string) => { const response = await fetch(`/api/new/investasi${(path) ? path : ''}`) return await response.json().catch(() => null) +} + +export const apiGetAllSahamSaya = async (path?: string) => { + const response = await fetch(`/api/new/investasi/invoice${(path) ? path : ''}`) + return await response.json().catch(() => null) } \ No newline at end of file diff --git a/src/app_modules/investasi/_lib/type_investasi.ts b/src/app_modules/investasi/_lib/type_investasi.ts index 1f8433b3..fed5c0ad 100644 --- a/src/app_modules/investasi/_lib/type_investasi.ts +++ b/src/app_modules/investasi/_lib/type_investasi.ts @@ -23,4 +23,15 @@ export interface IDataInvestasiBursa { targetDana: string pencarianInvestor: string updatedAt: Date +} + +export interface IDataSahamSaya { + id: string + nominal: string + lembarTerbeli: string + statusInvoiceId: string + createdAt: Date + statusInvoice: string + title: string + progress: string } \ No newline at end of file diff --git a/src/app_modules/investasi/_view/main/skeleton_saham_saya.tsx b/src/app_modules/investasi/_view/main/skeleton_saham_saya.tsx new file mode 100644 index 00000000..1aada275 --- /dev/null +++ b/src/app_modules/investasi/_view/main/skeleton_saham_saya.tsx @@ -0,0 +1,18 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Grid, Skeleton } from "@mantine/core"; + +export default function SkeletonInvestasiSahamSaya() { + return ( + <> + {[...Array(4)].map((_, index) => ( + + + + + + + + ))} + + ); +} \ No newline at end of file diff --git a/src/app_modules/investasi/_view/main/view_saham_saya_new.tsx b/src/app_modules/investasi/_view/main/view_saham_saya_new.tsx new file mode 100644 index 00000000..280f9990 --- /dev/null +++ b/src/app_modules/investasi/_view/main/view_saham_saya_new.tsx @@ -0,0 +1,73 @@ +'use client' +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; +import { Box, Center } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useState } from "react"; +import { Investasi_ComponentSahamSayaNew } from "../../_component/main/comp_card_saham_saya_new"; +import { apiGetAllSahamSaya } from "../../_lib/api_interface"; +import { IDataSahamSaya } from "../../_lib/type_investasi"; +import SkeletonInvestasiSahamSaya from "./skeleton_saham_saya"; + +export function Investasi_ViewSahamSayaNew() { + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true) + + + async function getDataSahamSaya() { + try { + setLoading(true) + const response = await apiGetAllSahamSaya(`?cat=saham-saya&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataSahamSaya() + }, []); + + return ( + <> + + { + loading ? + + : + _.isEmpty(data) ? ( + + ) : ( + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllSahamSaya(`?cat=saham-saya&page=${pageNew}`) + setActivePage((val) => val + 1); + + return loadData.data; + }} + > + {(item) => } +
+ ) + } +
+ + ); +} diff --git a/src/app_modules/investasi/_view/main/view_transaksi_new.tsx b/src/app_modules/investasi/_view/main/view_transaksi_new.tsx new file mode 100644 index 00000000..87726193 --- /dev/null +++ b/src/app_modules/investasi/_view/main/view_transaksi_new.tsx @@ -0,0 +1,72 @@ +'use client' +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import { Box, Center, Loader } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useState } from "react"; +import { Investasi_ComponentCardDaftarTransaksiNew } from "../../_component/main/comp_card_daftar_transaksi_new"; +import { apiGetAllSahamSaya } from "../../_lib/api_interface"; +import { IDataSahamSaya } from "../../_lib/type_investasi"; +import SkeletonInvestasiSahamSaya from "./skeleton_saham_saya"; + +export function Investasi_ViewDaftarTransaksiNew() { + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true) + + + async function getDataTransaksi() { + try { + setLoading(true) + const response = await apiGetAllSahamSaya(`?cat=transaksi&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataTransaksi() + }, []); + + return ( + <> + { + loading ? + + : + _.isEmpty(data) ? ( + + ) : ( + + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllSahamSaya(`?cat=transaksi&page=${pageNew}`) + setActivePage((val) => val + 1); + + return loadData.data; + }} + > + {(item) => } +
+
+ ) + } + + ); +} From 2322ab8444ef8dd9b2c06a1e70cade691fb6bfc2 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 19 Dec 2024 06:34:12 +0800 Subject: [PATCH 21/23] Fix notifikasi server action to API --- src/app/api/collaboration/master/route.ts | 20 ++ src/app/api/notifikasi/count/route.ts | 28 +++ .../notifikasi/get-all-by-category/route.ts | 70 +++---- src/app/api/notifikasi/master/route.ts | 35 ++++ src/app/dev/(user)/home/page.tsx | 2 +- src/app/dev/colab/create/page.tsx | 5 +- src/app/dev/notifikasi/collaboration/page.tsx | 10 +- src/app/dev/notifikasi/donasi/page.tsx | 10 +- src/app/dev/notifikasi/event/page.tsx | 6 +- src/app/dev/notifikasi/forum/page.tsx | 10 +- src/app/dev/notifikasi/investasi/page.tsx | 10 +- src/app/dev/notifikasi/job/page.tsx | 10 +- src/app/dev/notifikasi/layout.tsx | 4 - src/app/dev/notifikasi/semua/page.tsx | 4 +- src/app/dev/notifikasi/voting/page.tsx | 10 +- .../api_user_router/route_api_notifikasi.ts | 4 + src/app/lib/global_state.ts | 2 +- src/app/lib/prisma.ts | 2 +- src/app_modules/colab/component/index.ts | 3 + .../colab/component/lib/api_collaboration.ts | 4 + .../colab/component/skeleton_view.tsx | 28 +++ src/app_modules/colab/create/index.tsx | 55 +++++- src/app_modules/home/component/body_home.tsx | 36 ++-- .../home/component/footer_home.tsx | 5 + src/app_modules/home/view_home_new.tsx | 134 +++++++++++++ src/app_modules/notifikasi/_ui/index.ts | 21 +-- .../notifikasi/_ui/ui_all_notifikasi.tsx | 92 --------- .../_ui/ui_collaboration_notifikasi.tsx | 61 ------ .../notifikasi/_ui/ui_donasi_notifikasi.tsx | 61 ------ .../notifikasi/_ui/ui_forum_notifikasi.tsx | 61 ------ .../_ui/ui_investasi_notifikasi.tsx | 61 ------ .../notifikasi/_ui/ui_job_notifikasi.tsx | 74 -------- .../_ui/ui_new_layout_notifikasi.tsx | 105 ++++++++--- .../notifikasi/_ui/ui_new_notifikasi.tsx | 80 -------- ...event_notifikasi.tsx => ui_notifikasi.tsx} | 43 +++-- .../notifikasi/_ui/ui_voting_notifikasi.tsx | 61 ------ .../notifikasi/lib/api_notifikasi.ts | 10 + src/app_modules/notifikasi/ui/index.ts | 5 - .../notifikasi/ui/ui_layout_notifikasi.tsx | 91 --------- .../notifikasi/ui/ui_notifiaksi.tsx | 176 ------------------ src/app_modules/notifikasi/view/index.ts | 3 - .../notifikasi/view/view_notifikasi.tsx | 25 --- src/middleware.ts | 1 + 43 files changed, 507 insertions(+), 1031 deletions(-) create mode 100644 src/app/api/collaboration/master/route.ts create mode 100644 src/app/api/notifikasi/count/route.ts create mode 100644 src/app/api/notifikasi/master/route.ts create mode 100644 src/app_modules/colab/component/index.ts create mode 100644 src/app_modules/colab/component/lib/api_collaboration.ts create mode 100644 src/app_modules/colab/component/skeleton_view.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_all_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_collaboration_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_donasi_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_forum_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_investasi_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_job_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_new_notifikasi.tsx rename src/app_modules/notifikasi/_ui/{ui_event_notifikasi.tsx => ui_notifikasi.tsx} (69%) delete mode 100644 src/app_modules/notifikasi/_ui/ui_voting_notifikasi.tsx create mode 100644 src/app_modules/notifikasi/lib/api_notifikasi.ts delete mode 100644 src/app_modules/notifikasi/ui/index.ts delete mode 100644 src/app_modules/notifikasi/ui/ui_layout_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/ui/ui_notifiaksi.tsx delete mode 100644 src/app_modules/notifikasi/view/index.ts delete mode 100644 src/app_modules/notifikasi/view/view_notifikasi.tsx diff --git a/src/app/api/collaboration/master/route.ts b/src/app/api/collaboration/master/route.ts new file mode 100644 index 00000000..fb1b53da --- /dev/null +++ b/src/app/api/collaboration/master/route.ts @@ -0,0 +1,20 @@ +import { prisma } from "@/app/lib"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; +export async function GET(request: Request) { + try { + const data = await prisma.projectCollaborationMaster_Industri.findMany({}); + return NextResponse.json( + { success: true, message: "Berhasil mendapatkan data", data: data }, + { status: 200 } + ); + } catch (error) { + backendLogger.error("Master Collaboration:=========>", error); + return NextResponse.json( + { success: false, message: "Gagal mendapatkan data" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/notifikasi/count/route.ts b/src/app/api/notifikasi/count/route.ts new file mode 100644 index 00000000..c54ea290 --- /dev/null +++ b/src/app/api/notifikasi/count/route.ts @@ -0,0 +1,28 @@ +import { prisma } from "@/app/lib"; +import { newFunGetUserId } from "@/app/lib/new_fun_user_id"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; + +export async function GET(request: Request) { + try { + const userLoginId = await newFunGetUserId(); + + const count = await prisma.notifikasi.findMany({ + where: { + userId: userLoginId, + isRead: false, + userRoleId: "1", + }, + }); + + return NextResponse.json({ success: true, data: count.length }); + } catch (error) { + backendLogger.error("Gagal mendapatkan data count notifikasi", error); + return NextResponse.json( + { success: false, message: "Gagal mendapatkan data" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/notifikasi/get-all-by-category/route.ts b/src/app/api/notifikasi/get-all-by-category/route.ts index b13ce671..bfa9aefd 100644 --- a/src/app/api/notifikasi/get-all-by-category/route.ts +++ b/src/app/api/notifikasi/get-all-by-category/route.ts @@ -7,37 +7,34 @@ import { NextResponse } from "next/server"; export const dynamic = "force-dynamic"; export async function GET(request: Request) { - if (request.method === "GET") { - try { - const { searchParams } = new URL(request.url); - const category = searchParams.get("category") as ICategoryapp; - const page = searchParams.get("page"); + try { + let fixData; + const { searchParams } = new URL(request.url); + const category = searchParams.get("category") as ICategoryapp; + const page = searchParams.get("page"); - const userLoginId = await newFunGetUserId(); - const fixPage = _.toNumber(page); - const takeData = 10; - const skipData = fixPage * takeData - takeData; + const userLoginId = await newFunGetUserId(); + const fixPage = _.toNumber(page); + const takeData = 10; + const skipData = fixPage * takeData - takeData; - if (category === "Semua") { - const data = await prisma.notifikasi.findMany({ - take: takeData, - skip: skipData, - orderBy: [ - { - isRead: "asc", - }, - { createdAt: "desc" }, - ], - where: { - userId: userLoginId, - userRoleId: "1", + if (category === "Semua") { + fixData = await prisma.notifikasi.findMany({ + take: takeData, + skip: skipData, + orderBy: [ + { + isRead: "asc", }, - }); - - return NextResponse.json({ success: true, data }); - } - - const allData = await prisma.notifikasi.findMany({ + { createdAt: "desc" }, + ], + where: { + userId: userLoginId, + userRoleId: "1", + }, + }); + } else { + fixData = await prisma.notifikasi.findMany({ take: takeData, skip: skipData, orderBy: [ @@ -52,12 +49,17 @@ export async function GET(request: Request) { kategoriApp: _.upperCase(category), }, }); - - return NextResponse.json({ success: true, data: allData }); - } catch (error) { - backendLogger.error("Error get data notifikasi: " + error); } - } else { - return NextResponse.json({ success: false, message: "Method not allowed" }); + + return NextResponse.json( + { success: true, data: fixData, message: "Berhasil mendapatkan data" }, + { status: 200 } + ); + } catch (error) { + backendLogger.error("Error get data notifikasi: " + error); + return NextResponse.json( + { success: false, message: "Gagal mendapatkan data" }, + { status: 500 } + ); } } diff --git a/src/app/api/notifikasi/master/route.ts b/src/app/api/notifikasi/master/route.ts new file mode 100644 index 00000000..27e4145e --- /dev/null +++ b/src/app/api/notifikasi/master/route.ts @@ -0,0 +1,35 @@ +import { prisma } from "@/app/lib"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; + +export async function GET(request: Request) { + try { + const data = await prisma.masterKategoriApp.findMany({ + where: { + isActive: true, + }, + }); + + data.unshift({ + id: "0", + isActive: true, + createdAt: new Date(), + updatedAt: new Date(), + name: "Semua", + value: null, + }); + + return NextResponse.json( + { success: true, data, message: "Berhasil mendapatkan data" }, + { status: 200 } + ); + } catch (error) { + backendLogger.error("Master Notifikasi:", error); + return NextResponse.json( + { success: false, message: "Gagal mendapatkan data" }, + { status: 500 } + ); + } +} diff --git a/src/app/dev/(user)/home/page.tsx b/src/app/dev/(user)/home/page.tsx index 4bd78e32..c0fdc4c2 100644 --- a/src/app/dev/(user)/home/page.tsx +++ b/src/app/dev/(user)/home/page.tsx @@ -15,7 +15,7 @@ export default async function PageHome() { dataJob={dataJob as any} countNotifikasi={countNotifikasi} /> */} - + ); } diff --git a/src/app/dev/colab/create/page.tsx b/src/app/dev/colab/create/page.tsx index f1d84a65..6a530d6f 100644 --- a/src/app/dev/colab/create/page.tsx +++ b/src/app/dev/colab/create/page.tsx @@ -1,12 +1,9 @@ import { Colab_Create } from "@/app_modules/colab"; -import colab_funGetMasterIndustri from "@/app_modules/colab/fun/master/fun_get_master_industri"; export default async function Page() { - const listIndustri = await colab_funGetMasterIndustri(); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/collaboration/page.tsx b/src/app/dev/notifikasi/collaboration/page.tsx index edf580a3..ca9f64eb 100644 --- a/src/app/dev/notifikasi/collaboration/page.tsx +++ b/src/app/dev/notifikasi/collaboration/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiCollaboration } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Collaboration", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/donasi/page.tsx b/src/app/dev/notifikasi/donasi/page.tsx index 9af63325..ca9f64eb 100644 --- a/src/app/dev/notifikasi/donasi/page.tsx +++ b/src/app/dev/notifikasi/donasi/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiDonasi } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Donasi", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/event/page.tsx b/src/app/dev/notifikasi/event/page.tsx index 3b7b8e34..d4f0d3a1 100644 --- a/src/app/dev/notifikasi/event/page.tsx +++ b/src/app/dev/notifikasi/event/page.tsx @@ -1,11 +1,9 @@ -import { Notifikasi_UiEvent } from "@/app_modules/notifikasi/_ui"; +import Notifikasi_UiMain from "@/app_modules/notifikasi/_ui/ui_notifikasi"; export default async function Page() { - // - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/forum/page.tsx b/src/app/dev/notifikasi/forum/page.tsx index a27ffea2..ca9f64eb 100644 --- a/src/app/dev/notifikasi/forum/page.tsx +++ b/src/app/dev/notifikasi/forum/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiForum } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Forum", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/investasi/page.tsx b/src/app/dev/notifikasi/investasi/page.tsx index d5704862..ca9f64eb 100644 --- a/src/app/dev/notifikasi/investasi/page.tsx +++ b/src/app/dev/notifikasi/investasi/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiDonasi, Notifikasi_UiInvestasi } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Investasi", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/job/page.tsx b/src/app/dev/notifikasi/job/page.tsx index 029dbb80..ca9f64eb 100644 --- a/src/app/dev/notifikasi/job/page.tsx +++ b/src/app/dev/notifikasi/job/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiJob } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Job", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/layout.tsx b/src/app/dev/notifikasi/layout.tsx index 8b1e90fc..9e9d2461 100644 --- a/src/app/dev/notifikasi/layout.tsx +++ b/src/app/dev/notifikasi/layout.tsx @@ -1,19 +1,15 @@ import { UIGlobal_LayoutHeaderTamplate } from "@/app_modules/_global/ui"; import { Notifikasi_UiNewLayout } from "@/app_modules/notifikasi/_ui"; -import { notifikasi_funGetKategoriApp } from "@/app_modules/notifikasi/fun/get/fun_get_kategori_app"; export default async function Layout({ children, }: { children: React.ReactNode; }) { - const masterKategori = await notifikasi_funGetKategoriApp(); - return ( <> } - masterKategori={masterKategori} > {children} diff --git a/src/app/dev/notifikasi/semua/page.tsx b/src/app/dev/notifikasi/semua/page.tsx index 2f691b3c..ca9f64eb 100644 --- a/src/app/dev/notifikasi/semua/page.tsx +++ b/src/app/dev/notifikasi/semua/page.tsx @@ -1,9 +1,9 @@ -import { Notifikasi_UiAll } from "@/app_modules/notifikasi/_ui"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { return ( <> - + ); } diff --git a/src/app/dev/notifikasi/voting/page.tsx b/src/app/dev/notifikasi/voting/page.tsx index 1dd4b042..ca9f64eb 100644 --- a/src/app/dev/notifikasi/voting/page.tsx +++ b/src/app/dev/notifikasi/voting/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiVoting } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Voting", - }); - return ( <> - + ); } diff --git a/src/app/lib/api_user_router/route_api_notifikasi.ts b/src/app/lib/api_user_router/route_api_notifikasi.ts index 94027735..0fccc1b0 100644 --- a/src/app/lib/api_user_router/route_api_notifikasi.ts +++ b/src/app/lib/api_user_router/route_api_notifikasi.ts @@ -8,4 +8,8 @@ export const API_RouteNotifikasi = { category: ICategoryapp; page: number; }) => `/api/notifikasi/get-all-by-category?category=${category}&page=${page}`, + + get_master_kategori: () => `/api/notifikasi/master`, + + get_count_by_id: () => `/api/notifikasi/count`, }; diff --git a/src/app/lib/global_state.ts b/src/app/lib/global_state.ts index 59259c1c..e49b6643 100644 --- a/src/app/lib/global_state.ts +++ b/src/app/lib/global_state.ts @@ -42,7 +42,7 @@ export type IRealtimeData = { export const gs_realtimeData = atom(null); export const gs_admin_ntf = atom(0); export const gs_user_ntf = atom(0); -export const gs_count_ntf = atom(0); +export const gs_count_ntf = atom(null); // job export const gs_adminJob_triggerReview = atom(false); diff --git a/src/app/lib/prisma.ts b/src/app/lib/prisma.ts index 101fd93b..07ccb3e8 100644 --- a/src/app/lib/prisma.ts +++ b/src/app/lib/prisma.ts @@ -25,6 +25,6 @@ process.on("SIGINT", async () => { process.exit(0); }); -// console.log("==> Test prisma"); +console.log("==> Test prisma"); export default prisma; diff --git a/src/app_modules/colab/component/index.ts b/src/app_modules/colab/component/index.ts new file mode 100644 index 00000000..e17745cf --- /dev/null +++ b/src/app_modules/colab/component/index.ts @@ -0,0 +1,3 @@ +import { Collaboration_SkeletonCreate } from "./skeleton_view"; + +export { Collaboration_SkeletonCreate }; diff --git a/src/app_modules/colab/component/lib/api_collaboration.ts b/src/app_modules/colab/component/lib/api_collaboration.ts new file mode 100644 index 00000000..bfffe8d5 --- /dev/null +++ b/src/app_modules/colab/component/lib/api_collaboration.ts @@ -0,0 +1,4 @@ +export async function apiGetMasterCollaboration() { + const data = await fetch(`/api/collaboration/master`); + return await data.json().catch(() => null); +} diff --git a/src/app_modules/colab/component/skeleton_view.tsx b/src/app_modules/colab/component/skeleton_view.tsx new file mode 100644 index 00000000..1061a1af --- /dev/null +++ b/src/app_modules/colab/component/skeleton_view.tsx @@ -0,0 +1,28 @@ +import { Skeleton, Stack } from "@mantine/core"; + +export function Collaboration_SkeletonCreate() { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/app_modules/colab/create/index.tsx b/src/app_modules/colab/create/index.tsx index 32c32541..b28e3236 100644 --- a/src/app_modules/colab/create/index.tsx +++ b/src/app_modules/colab/create/index.tsx @@ -5,7 +5,15 @@ import ComponentGlobal_InputCountDown from "@/app_modules/_global/component/inpu import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan"; -import { Button, Select, Stack, TextInput, Textarea } from "@mantine/core"; +import { + Button, + Center, + Select, + Stack, + TextInput, + Textarea, + Loader, +} from "@mantine/core"; import { useRouter } from "next/navigation"; import { useState } from "react"; import colab_funCreateProyek from "../fun/create/fun_create_proyek"; @@ -13,12 +21,12 @@ import { MODEL_COLLABORATION_MASTER } from "../model/interface"; import mqtt_client from "@/util/mqtt_client"; import { useHookstate } from "@hookstate/core"; import { useGsCollabCreate } from "../global_state/state"; +import { useShallowEffect } from "@mantine/hooks"; +import { apiGetMasterCollaboration } from "../component/lib/api_collaboration"; +import { clientLogger } from "@/util/clientLogger"; +import { Collaboration_SkeletonCreate } from "../component"; -export default function Colab_Create({ - listIndustri, -}: { - listIndustri: MODEL_COLLABORATION_MASTER[]; -}) { +export default function Colab_Create() { const [value, setValue] = useState({ title: "", lokasi: "", @@ -27,9 +35,38 @@ export default function Colab_Create({ projectCollaborationMaster_IndustriId: 0, // jumlah_partisipan: 0, }); + + const [listIndustri, setListIndustri] = useState< + MODEL_COLLABORATION_MASTER[] | null + >(null); + + useShallowEffect(() => { + onLoadMaster(); + }, []); + + async function onLoadMaster() { + try { + const respone = await apiGetMasterCollaboration(); + if (respone.success) { + setListIndustri(respone.data); + } + } catch (error) { + clientLogger.error("Error get master collaboration", error); + } + } + + + if (listIndustri == null) { + return ( + <> + + + ); + } + return ( <> - + - setFilter(e.target.value as any)} + className={styles.select} + > + + + + + + + +
+ {filteredLogs.map((log, index) => ( +
+
+ + {format(new Date(log.timestamp), "yyyy-MM-dd HH:mm:ss")} + + + {log.level.toUpperCase()} + +
+ +
{log.message}
+ + {log.metadata && ( +
+                {JSON.stringify(log.metadata, null, 2)}
+              
+ )} +
+ ))} +
+ + ); +} diff --git a/src/app/api/logs/view/route.ts b/src/app/api/logs/view/route.ts new file mode 100644 index 00000000..b660e323 --- /dev/null +++ b/src/app/api/logs/view/route.ts @@ -0,0 +1,71 @@ +// app/api/logs/view/route.ts +import { NextRequest, NextResponse } from "next/server"; +import fs from "fs/promises"; +import path from "path"; + +interface LogEntry { + timestamp: string; + level: string; + message: string; + metadata?: any; +} + +async function readLogFiles(directory: string): Promise { + try { + const logPath = path.join(process.cwd(), directory); + const files = await fs.readdir(logPath); + const logFiles = files.filter((file) => file.endsWith(".log")); + + const allLogs: LogEntry[] = []; + + for (const file of logFiles) { + const filePath = path.join(logPath, file); + const content = await fs.readFile(filePath, "utf-8"); + + // Parse setiap baris log + const logs = content + .split("\n") + .filter(Boolean) + .map((line) => { + try { + return JSON.parse(line); + } catch (e) { + return null; + } + }) + .filter(Boolean); + + allLogs.push(...logs); + } + + // Sort berdasarkan timestamp, terbaru di atas + return allLogs.sort( + (a, b) => + new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() + ); + } catch (error) { + console.error("Error reading log files:", error); + return []; + } +} + +export async function GET(request: NextRequest) { + try { + // Baca logs dari frontend dan backend + const frontendLogs = await readLogFiles("logs/frontend"); + const backendLogs = await readLogFiles("logs/backend"); + + // Gabungkan dan sort semua logs + const allLogs = [...frontendLogs, ...backendLogs].sort( + (a, b) => + new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() + ); + + return NextResponse.json({ logs: allLogs }); + } catch (error) { + return NextResponse.json( + { error: "Failed to fetch logs" }, + { status: 500 } + ); + } +} diff --git a/src/app/dev/(user)/home/page.tsx b/src/app/dev/(user)/home/page.tsx index c0fdc4c2..675c907a 100644 --- a/src/app/dev/(user)/home/page.tsx +++ b/src/app/dev/(user)/home/page.tsx @@ -1,21 +1,9 @@ import { HomeViewNew } from "@/app_modules/home"; -import notifikasi_countUserNotifikasi from "@/app_modules/notifikasi/fun/count/fun_count_by_id"; export default async function PageHome() { - // const userLoginId = await funGetUserIdByToken(); - // const dataUser = await user_getOneByUserId(userLoginId as string); - // const dataJob = await job_getTwoForHomeView(); - const countNotifikasi = await notifikasi_countUserNotifikasi(); - - return ( <> - {/* */} - + ); } diff --git a/src/app/dev/layout.tsx b/src/app/dev/layout.tsx index 49f4a4c7..bfb694c1 100644 --- a/src/app/dev/layout.tsx +++ b/src/app/dev/layout.tsx @@ -7,9 +7,13 @@ export default async function Layout({ }: { children: React.ReactNode; }) { + + const userId = await newFunGetUserId(); + return ( <> { process.exit(0); }); -console.log("==> Test prisma"); +// console.log("==> Test prisma"); export default prisma; diff --git a/src/app/lib/realtime_provider.tsx b/src/app/lib/realtime_provider.tsx index 07919a7f..3e25986b 100644 --- a/src/app/lib/realtime_provider.tsx +++ b/src/app/lib/realtime_provider.tsx @@ -17,8 +17,6 @@ import { gs_votingTiggerBeranda, IRealtimeData, } from "./global_state"; -import { newFunGetUserId } from "./new_fun_user_id"; -import { useState } from "react"; // const WIBU_REALTIME_TOKEN: string | undefined = // process.env.NEXT_PUBLIC_WIBU_REALTIME_TOKEN; @@ -28,15 +26,16 @@ export type TypeNotification = { type: "message" | "notification" | "trigger"; pushNotificationTo: "ADMIN" | "USER"; dataMessage?: IRealtimeData; - userLoginId?: string; + userId?: string; }; export default function RealtimeProvider({ + userId, WIBU_REALTIME_TOKEN, }: { + userId: string; WIBU_REALTIME_TOKEN: string; }) { - const [userLoginId, setUserLoginId] = useState(""); const [dataRealtime, setDataRealtime] = useAtom(gs_realtimeData); const [newAdminNtf, setNewAdminNtf] = useAtom(gs_admin_ntf); const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); @@ -72,15 +71,7 @@ export default function RealtimeProvider({ gs_donasiTriggerBeranda ); - async function loadUserId() { - const userId = await newFunGetUserId(); - - setUserLoginId(userId as string); - } - useShallowEffect(() => { - loadUserId(); - try { WibuRealtime.init({ project: "hipmi", @@ -97,7 +88,7 @@ export default function RealtimeProvider({ if ( data.type == "notification" && data.pushNotificationTo == "USER" && - data.dataMessage?.userId == userLoginId + data.dataMessage?.userId == userId ) { setNewUserNtf((e) => e + 1); setDataRealtime(data.dataMessage as any); @@ -144,7 +135,7 @@ export default function RealtimeProvider({ data.type == "notification" && data.pushNotificationTo == "USER" && data.dataMessage?.status == "Peserta Event" && - userLoginId !== data.dataMessage?.userId + userId !== data.dataMessage?.userId ) { setNewUserNtf((e) => e + 1); } @@ -172,7 +163,7 @@ export default function RealtimeProvider({ data.type == "notification" && data.pushNotificationTo == "USER" && data.dataMessage?.status == "Voting Masuk" && - userLoginId !== data.dataMessage?.userId + userId !== data.dataMessage?.userId ) { setNewUserNtf((e) => e + 1); } @@ -200,9 +191,9 @@ export default function RealtimeProvider({ // data.type == "notification" && // data.pushNotificationTo == "ADMIN" && // data.dataMessage?.status == "Menunggu" && - // userLoginId !== data.dataMessage?.userId + // userId !== data.dataMessage?.userId // ) { - // console.log("yes"); + // } // ---------------------- DONASI ------------------------- // diff --git a/src/app_modules/admin/job/child/reject/index.tsx b/src/app_modules/admin/job/child/reject/index.tsx index 8ba05154..ec3364a9 100644 --- a/src/app_modules/admin/job/child/reject/index.tsx +++ b/src/app_modules/admin/job/child/reject/index.tsx @@ -1,5 +1,6 @@ "use client"; +import { IRealtimeData } from "@/app/lib/global_state"; import { RouterAdminJob } from "@/app/lib/router_admin/router_admin_job"; import ComponentGlobal_InputCountDown from "@/app_modules/_global/component/input_countdown"; import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; @@ -8,7 +9,6 @@ import { ComponentAdminGlobal_TitlePage } from "@/app_modules/admin/_admin_globa import ComponentAdminGlobal_HeaderTamplate from "@/app_modules/admin/_admin_global/header_tamplate"; import adminNotifikasi_funCreateToUser from "@/app_modules/admin/notifikasi/fun/create/fun_create_notif_user"; import { MODEL_JOB } from "@/app_modules/job/model/interface"; -import mqtt_client from "@/util/mqtt_client"; import { Button, Center, @@ -22,11 +22,12 @@ import { Table, Text, TextInput, - Textarea + Textarea, } from "@mantine/core"; import { IconBan, IconPhotoCheck, IconSearch } from "@tabler/icons-react"; import { useRouter } from "next/navigation"; import { useState } from "react"; +import { WibuRealtime } from "wibu-pkg"; import { AdminJob_funEditCatatanById } from "../../fun/edit/fun_edit_catatan_by_id"; import adminJob_getListReject from "../../fun/get/get_list_reject"; @@ -311,7 +312,7 @@ async function onReject({ const loadData = await adminJob_getListReject({ page: 1 }); onSetData(loadData); - const dataNotif = { + const dataNotifikasi: IRealtimeData = { appId: reject.data?.id as any, status: reject.data?.MasterStatus?.name as any, userId: reject.data?.authorId as any, @@ -321,14 +322,15 @@ async function onReject({ }; const notif = await adminNotifikasi_funCreateToUser({ - data: dataNotif as any, + data: dataNotifikasi as any, }); if (notif.status === 201) { - mqtt_client.publish( - "USER", - JSON.stringify({ userId: reject?.data?.authorId, count: 1 }) - ); + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "USER", + dataMessage: dataNotifikasi, + }); } ComponentGlobal_NotifikasiBerhasil(reject.message); diff --git a/src/app_modules/home/view_home_new.tsx b/src/app_modules/home/view_home_new.tsx index e383952e..b6f4dd51 100644 --- a/src/app_modules/home/view_home_new.tsx +++ b/src/app_modules/home/view_home_new.tsx @@ -1,5 +1,7 @@ "use client"; +import { API_RouteNotifikasi } from "@/app/lib/api_user_router/route_api_notifikasi"; import { gs_count_ntf, gs_user_ntf } from "@/app/lib/global_state"; +import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi"; import { RouterUserSearch } from "@/app/lib/router_hipmi/router_user_search"; import { ActionIcon, Indicator, Text } from "@mantine/core"; @@ -9,46 +11,35 @@ import { useAtom } from "jotai"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { MainColor } from "../_global/color"; -import { ComponentGlobal_NotifikasiPeringatan } from "../_global/notif_global"; import UIGlobal_LayoutHeaderTamplate from "../_global/ui/ui_header_tamplate"; import UIGlobal_LayoutTamplate from "../_global/ui/ui_layout_tamplate"; -import notifikasi_countUserNotifikasi from "../notifikasi/fun/count/fun_count_by_id"; +import { gs_notifikasi_kategori_app } from "../notifikasi/lib"; import BodyHome from "./component/body_home"; import FooterHome from "./component/footer_home"; import { apiGetDataHome } from "./fun/get/api_home"; -import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; -import { gs_notifikasi_kategori_app } from "../notifikasi/lib"; -export default function HomeViewNew({ - countNotifikasi, -}: { - countNotifikasi: number; -}) { - const [countNtf, setCountNtf] = useState(countNotifikasi); +export default function HomeViewNew() { + const [countNtf, setCountNtf] = useAtom(gs_count_ntf); const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); - const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); const [dataUser, setDataUser] = useState({}); const [categoryPage, setCategoryPage] = useAtom(gs_notifikasi_kategori_app); const router = useRouter(); useShallowEffect(() => { - onLoadNotifikasi({ - onLoad(val) { - setCountNtf(val); - }, - }); - - setCountNtf(countLoadNtf as any); - }, [countLoadNtf, setCountNtf]); + onLoadNotifikasi(); + }, []); useShallowEffect(() => { - setCountNtf(countNtf + newUserNtf); - setNewUserNtf(0); - }, [newUserNtf, setCountNtf]); + if (countNtf != null) { + setCountNtf(countNtf + newUserNtf); + setNewUserNtf(0); + } + }, [newUserNtf, countNtf]); - async function onLoadNotifikasi({ onLoad }: { onLoad: (val: any) => void }) { - const loadNotif = await notifikasi_countUserNotifikasi(); - onLoad(loadNotif); + async function onLoadNotifikasi() { + const loadNotif = await fetch(API_RouteNotifikasi.get_count_by_id()); + const data = await loadNotif.json().then((res) => res.data); + setCountNtf(data); } useShallowEffect(() => { @@ -75,6 +66,7 @@ export default function HomeViewNew({ customButtonLeft={ { if ( @@ -93,6 +85,7 @@ export default function HomeViewNew({ customButtonRight={ { if ( dataUser.profile === undefined || @@ -100,24 +93,23 @@ export default function HomeViewNew({ ) { router.push(RouterProfile.create, { scroll: false }); } else { - setCategoryPage("Semua") + setCategoryPage("Semua"); router.push( RouterNotifikasi.categoryApp({ name: "semua" }), { scroll: false, } ); - } }} > - {countNotifikasi > 0 ? ( + {countNtf != null && countNtf > 0 ? ( - {countNotifikasi > 99 ? "99+" : countNotifikasi} + {countNtf > 99 ? "99+" : countNtf} } > @@ -137,137 +129,3 @@ export default function HomeViewNew({ ); } - -// "use client"; -// import { API_RouteNotifikasi } from "@/app/lib/api_user_router/route_api_notifikasi"; -// import { gs_count_ntf, gs_user_ntf } from "@/app/lib/global_state"; -// import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; -// import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi"; -// import { RouterUserSearch } from "@/app/lib/router_hipmi/router_user_search"; -// import { ActionIcon, Indicator, Text } from "@mantine/core"; -// import { useShallowEffect } from "@mantine/hooks"; -// import { IconBell, IconUserSearch } from "@tabler/icons-react"; -// import { useAtom } from "jotai"; -// import { useRouter } from "next/navigation"; -// import { useState } from "react"; -// import { MainColor } from "../_global/color"; -// import UIGlobal_LayoutHeaderTamplate from "../_global/ui/ui_header_tamplate"; -// import UIGlobal_LayoutTamplate from "../_global/ui/ui_layout_tamplate"; -// import { gs_notifikasi_kategori_app } from "../notifikasi/lib"; -// import BodyHome from "./component/body_home"; -// import FooterHome from "./component/footer_home"; -// import { apiGetDataHome } from "./fun/get/api_home"; - -// export default function HomeViewNew() { -// const [countNtf, setCountNtf] = useAtom(gs_count_ntf); -// const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); -// const [dataUser, setDataUser] = useState({}); -// const [categoryPage, setCategoryPage] = useAtom(gs_notifikasi_kategori_app); -// const router = useRouter(); - -// useShallowEffect(() => { -// onLoadNotifikasi(); -// }, []); - -// useShallowEffect(() => { -// if (countNtf != null) { -// setCountNtf(countNtf + newUserNtf); -// setNewUserNtf(0); -// } -// console.log("notif baru", newUserNtf); -// console.log("notif baru", countNtf); -// }, [newUserNtf, countNtf]); - -// async function onLoadNotifikasi() { -// const loadNotif = await fetch(API_RouteNotifikasi.get_count_by_id()); -// const data = await loadNotif.json().then((res) => res.data); -// setCountNtf(data); -// } - -// useShallowEffect(() => { -// cekUserLogin(); -// }, []); - -// async function cekUserLogin() { -// try { -// const response = await apiGetDataHome("?cat=cek_profile"); -// if (response.success) { -// setDataUser(response.data); -// } -// } catch (error) { -// console.error(error); -// } -// } - -// return ( -// <> -// { -// if ( -// dataUser.profile === undefined || -// dataUser?.profile === null -// ) { -// router.push(RouterProfile.create, { scroll: false }); -// } else { -// router.push(RouterUserSearch.main, { scroll: false }); -// } -// }} -// > -// -// -// } -// customButtonRight={ -// { -// if ( -// dataUser.profile === undefined || -// dataUser?.profile === null -// ) { -// router.push(RouterProfile.create, { scroll: false }); -// } else { -// setCategoryPage("Semua"); -// router.push( -// RouterNotifikasi.categoryApp({ name: "semua" }), -// { -// scroll: false, -// } -// ); -// } -// }} -// > -// {countNtf != null && countNtf > 0 ? ( -// -// {countNtf > 99 ? "99+" : countNtf} -// -// } -// > -// -// -// ) : ( -// -// )} -// -// } -// /> -// } -// footer={} -// > -// -// -// -// ); -// }