Desc:
- Fitur crowdfunding
- Fitur Investasi
- Fitur tambah investasi
No issue
This commit is contained in:
2023-10-20 10:15:17 +08:00
parent 66fcad8634
commit bff07e84b1
47 changed files with 1172 additions and 49 deletions

View File

@@ -12,6 +12,7 @@
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",
"@emotion/server": "^11.11.0", "@emotion/server": "^11.11.0",
"@mantine/core": "^6.0.17", "@mantine/core": "^6.0.17",
"@mantine/dropzone": "^7.1.3",
"@mantine/hooks": "^6.0.17", "@mantine/hooks": "^6.0.17",
"@mantine/next": "^6.0.17", "@mantine/next": "^6.0.17",
"@prisma/client": "^5.0.0", "@prisma/client": "^5.0.0",

View File

@@ -21,6 +21,7 @@ model User {
masterUserRoleId String @default("1") masterUserRoleId String @default("1")
UserSession UserSession? UserSession UserSession?
Profile Profile? Profile Profile?
Investasi Investasi[]
} }
model MasterUserRole { model MasterUserRole {
@@ -60,12 +61,13 @@ model Profile {
} }
model Images { model Images {
id String @id @default(cuid()) id String @id @default(cuid())
url String url String
active Boolean @default(true) active Boolean @default(true)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
Profile Profile? Profile Profile?
Investasi Investasi?
} }
model Katalog { model Katalog {
@@ -73,12 +75,12 @@ model Katalog {
namaBisnis String namaBisnis String
alamatKantor String alamatKantor String
tlpn String tlpn String
deskripsi String deskripsi String
active Boolean @default(true) active Boolean @default(true)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
Profile Profile? @relation(fields: [profileId], references: [id]) Profile Profile? @relation(fields: [profileId], references: [id])
profileId String? profileId String?
MasterBidangBisnis MasterBidangBisnis @relation(fields: [masterBidangBisnisId], references: [id]) MasterBidangBisnis MasterBidangBisnis @relation(fields: [masterBidangBisnisId], references: [id])
masterBidangBisnisId String masterBidangBisnisId String
} }
@@ -91,3 +93,54 @@ model MasterBidangBisnis {
updatedAt DateTime @default(now()) @updatedAt updatedAt DateTime @default(now()) @updatedAt
Katalog Katalog[] Katalog Katalog[]
} }
model Investasi {
id String @id @default(cuid())
title String
targetDana String
hargaLembar String
totalLembar String
roi String
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
author User? @relation(fields: [authorId], references: [id])
authorId String?
MasterPeriodeDeviden MasterPeriodeDeviden? @relation(fields: [masterPeriodeDevidenId], references: [id])
masterPeriodeDevidenId String?
MasterPembagianDeviden MasterPembagianDeviden? @relation(fields: [masterPembagianDevidenId], references: [id])
masterPembagianDevidenId String?
MasterPencarianInvestor MasterPencarianInvestor? @relation(fields: [masterPencarianInvestorId], references: [id])
masterPencarianInvestorId String?
ImageInvestasi Images? @relation(fields: [imagesId], references: [id])
imagesId String? @unique
}
model MasterPencarianInvestor {
id String @id @default(cuid())
name String
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
investasi Investasi[]
}
model MasterPeriodeDeviden {
id String @id @default(cuid())
name String
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
investasi Investasi[]
}
model MasterPembagianDeviden {
id String @id @default(cuid())
name String
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
investasi Investasi[]
}

BIN
public/aset/no-img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

View File

@@ -0,0 +1,39 @@
import { NextRequest, NextResponse } from "next/server";
import fs from "fs";
import prisma from "@/app/lib/prisma";
export async function GET(
req: NextRequest,
{ params }: { params: { id: string } }
) {
console.log(params.id)
const data = await prisma.images.findUnique({
where: {
id: params.id,
},
select: {
url: true,
},
});
console.log(data)
// return data
if (!fs.existsSync(`./public/investasi/${data?.url}`)) {
const fl = fs.readFileSync(`./public/aset/no-img.png`);
return new NextResponse(fl, {
headers: {
"Content-Type": "image/png",
},
});
}
const fl = fs.readFileSync(`./public/investasi/${data?.url}`);
return new NextResponse(fl, {
headers: {
"Content-Type": "image/png",
},
});
}

View File

@@ -2,6 +2,9 @@ import prisma from "@/app/lib/prisma";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import userRole from "../../../bin/seeder/user_role.json"; import userRole from "../../../bin/seeder/user_role.json";
import bidangBisnis from "../../../bin/seeder/bidang_bisnis.json"; import bidangBisnis from "../../../bin/seeder/bidang_bisnis.json";
import pencarianInvestor from "./../../../bin/seeder/investasi/pencarian_investor.json";
import periodeDeviden from "./../../../bin/seeder/investasi/periode_deviden.json";
import pembagianDeviden from "./../../../bin/seeder/investasi/pembagian_deviden.json";
export async function GET(req: Request) { export async function GET(req: Request) {
const dev = new URL(req.url).searchParams.get("dev"); const dev = new URL(req.url).searchParams.get("dev");
@@ -37,6 +40,55 @@ export async function GET(req: Request) {
}, },
}); });
} }
for (let i of pencarianInvestor) {
await prisma.masterPencarianInvestor.upsert({
where: {
id: i.id.toString(),
},
update: {
id: i.id.toString(),
name: i.name,
},
create: {
id: i.id.toString(),
name: i.name,
},
});
}
for (let i of pembagianDeviden) {
await prisma.masterPembagianDeviden.upsert({
where: {
id: i.id.toString(),
},
update: {
id: i.id.toString(),
name: i.name,
},
create: {
id: i.id.toString(),
name: i.name,
},
});
}
for (let i of periodeDeviden) {
await prisma.masterPeriodeDeviden.upsert({
where: {
id: i.id.toString(),
},
update: {
id: i.id.toString(),
name: i.name,
},
create: {
id: i.id.toString(),
name: i.name,
},
});
}
return NextResponse.json({ success: true }); return NextResponse.json({ success: true });
} }

View File

@@ -0,0 +1,14 @@
import { LayoutMainCrowd } from "@/app_modules/crowd";
import React from "react";
export default async function Layout({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<LayoutMainCrowd>{children}</LayoutMainCrowd>
</>
);
}

View File

@@ -0,0 +1,9 @@
import { MainCrowd } from "@/app_modules/crowd";
export default async function Page() {
return (
<>
<MainCrowd />
</>
);
}

View File

@@ -0,0 +1,7 @@
import { SplashCrowd } from "@/app_modules/crowd";
export default async function Page() {
return <>
<SplashCrowd/>
</>
}

View File

@@ -0,0 +1,14 @@
import { InvestasiCreateLayout } from "@/app_modules/investasi";
import React from "react";
export default async function Layout({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<InvestasiCreateLayout>{children}</InvestasiCreateLayout>
</>
);
}

View File

@@ -0,0 +1,37 @@
import { InvestasiCreate } from "@/app_modules/investasi";
import { unsealData } from "iron-session";
import { cookies } from "next/headers";
import yaml from "yaml";
import fs from "fs";
import { funCreateInvestasi } from "@/app_modules/investasi/fun/fun_create_investasi";
import getPencarianInvestor from "@/app_modules/investasi/fun/get_pencarian_investor";
import getPeriodeDeviden from "@/app_modules/investasi/fun/get_periode_deviden";
import getPembagianDeviden from "@/app_modules/investasi/fun/get_pembagian_deviden";
const config = yaml.parse(fs.readFileSync("config.yaml").toString());
export default async function Page() {
const c = cookies().get("ssn");
const tkn = JSON.parse(
await unsealData(c?.value as string, {
password: config.server.password,
})
);
const pencarianInvestor = await getPencarianInvestor();
const periodeDeviden = await getPeriodeDeviden();
const pembagianDeviden = await getPembagianDeviden();
// console.log(pembagianDeviden)
return (
<>
<InvestasiCreate
id={tkn.id}
pencarianInvestor={pencarianInvestor as any}
periodeDeviden={periodeDeviden as any}
pembagianDeviden={pembagianDeviden as any}
/>
</>
);
}

View File

@@ -0,0 +1,8 @@
import { LayoutMainInvestasi } from "@/app_modules/investasi";
import React from "react";
export default async function Layout({children}: {children: React.ReactNode}) {
return <>
<LayoutMainInvestasi>{children}</LayoutMainInvestasi>
</>
}

View File

@@ -0,0 +1,24 @@
import { MainInvestasi } from "@/app_modules/investasi";
import { getListAllInvestasi } from "@/app_modules/investasi/fun/get_list_all_investasi";
import getPembagianDeviden from "@/app_modules/investasi/fun/get_pembagian_deviden";
import getPencarianInvestor from "@/app_modules/investasi/fun/get_pencarian_investor";
import getPeriodeDeviden from "@/app_modules/investasi/fun/get_periode_deviden";
export default async function Page() {
const data = await getListAllInvestasi()
const pencarianInvestor = await getPencarianInvestor();
const periodeDeviden = await getPeriodeDeviden();
const pembagianDeviden = await getPembagianDeviden();
// console.log(data)
return <>
<MainInvestasi
listData={data as any}
pencarianInvestor={pencarianInvestor as any}
periodeDeviden={periodeDeviden as any}
pembagianDeviden={pembagianDeviden as any}
/>
</>
}

View File

@@ -0,0 +1,8 @@
import { LayoutUploadGambarInvestasi } from "@/app_modules/investasi";
import React from "react";
export default async function Layout({children}: {children: React.ReactNode}) {
return<>
<LayoutUploadGambarInvestasi>{children}</LayoutUploadGambarInvestasi>
</>
}

View File

@@ -0,0 +1,7 @@
import { UploadGambarInvestasi } from "@/app_modules/investasi";
export default async function Page() {
return<>
<UploadGambarInvestasi/>
</>
}

View File

@@ -14,4 +14,7 @@ export const ApiHipmi = {
//Portofolio //Portofolio
create_portofolio: "/api/portofolio/create", create_portofolio: "/api/portofolio/create",
//Investasi
get_gambar_investasi: "/api/investasi/gambar"
}; };

View File

@@ -0,0 +1,54 @@
"use client";
import { Header, Group, ActionIcon, Text } from "@mantine/core";
import { IconArrowLeft } from "@tabler/icons-react";
import { useRouter } from "next/navigation";
export default function HeaderTamplate({
route,
route2,
title,
icon,
}: {
route?: any;
route2?: any;
title: string;
icon?: any;
}) {
const router = useRouter();
return (
<>
<Header height={50}>
<Group h={50} position="apart" px={"md"}>
<ActionIcon
variant="transparent"
onClick={() => {
if (route === null || route === undefined) {
return router.back();
} else {
return router.push(route);
}
}}
>
<IconArrowLeft />
</ActionIcon>
<Text>{title}</Text>
{(() => {
if (route2 === null ||route2 === undefined) {
return <ActionIcon disabled variant="transparent"></ActionIcon>;
} else {
return (
<ActionIcon
variant="transparent"
onClick={() => router.push(route2)}
>
{icon}
</ActionIcon>
);
}
})()}
</Group>
</Header>
</>
);
}

View File

@@ -0,0 +1,5 @@
import MainCrowd from "./main/view";
import LayoutMainCrowd from "./main/layout";
import SplashCrowd from "./splash/view";
export {MainCrowd,LayoutMainCrowd, SplashCrowd}

View File

@@ -0,0 +1,24 @@
"use client";
import HeaderTamplate from "@/app_modules/component/header_tamplate";
import { ActionIcon, AppShell, Group, Header, Text } from "@mantine/core";
import { IconArrowLeft } from "@tabler/icons-react";
import { useRouter } from "next/navigation";
import React from "react";
export default function LayoutMainCrowd({
children,
}: {
children: React.ReactNode;
}) {
const router = useRouter();
return (
<>
<AppShell
header={<HeaderTamplate route="/dev/home" title="Crowd Funding" />}
>
{children}
</AppShell>
</>
);
}

View File

@@ -0,0 +1,33 @@
"use client";
import { Button, Center, Stack, Text, Title } from "@mantine/core";
import { useRouter } from "next/navigation";
import toast from "react-simple-toasts";
export default function MainCrowd() {
const router = useRouter();
return (
<>
<Center>
<Stack>
<Text>Selamat datang di</Text>
<Text>HIPMI Crowd Funding</Text>
<Button
w={300}
bg={"green"}
onClick={() => router.push("/dev/investasi/main")}
>
Investasi
</Button>
<Button
w={300}
bg={"grape"}
onClick={() => toast("Cooming Soon Feature...")}
>
Donasi
</Button>
</Stack>
</Center>
</>
);
}

View File

@@ -0,0 +1,23 @@
"use client";
import { Center, Stack, Text, Title } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { useRouter } from "next/navigation";
export default function SplashCrowd() {
const router = useRouter();
useShallowEffect(() => {
setTimeout(() => router.push("/dev/crowd/main"), 2000);
}, []);
return (
<>
<Center h={"100vh"}>
<Stack>
<Text>Welcome to,</Text>
<Title>CrowdFunding</Title>
</Stack>
</Center>
</>
);
}

View File

@@ -47,48 +47,7 @@ import { funGetUserProfile } from "../fun/get_user_profile";
import { USER_PROFILE } from "../models/user_profile"; import { USER_PROFILE } from "../models/user_profile";
import AppNotif from "../notif"; import AppNotif from "../notif";
const listHalaman = [
{
id: 1,
name: "Forums",
icon: <IconMessages size={50} />,
},
{
id: 2,
name: "Project Collaboration",
icon: <IconAffiliate size={50} />,
},
{
id: 3,
name: "Voting",
icon: <IconPackageImport size={50} />,
},
{
id: 4,
name: "Event",
icon: <IconPresentation size={50} />,
},
{
id: 5,
name: "Crowd Funding",
icon: <IconHeartHandshake size={50} />,
},
{
id: 6,
name: "Marketplace",
icon: <IconShoppingBag size={50} />,
},
{
id: 7,
name: "Job Vacancy",
icon: <IconBriefcase size={50} />,
},
{
id: 8,
name: "Business Maps",
icon: <IconMap2 size={50} />,
},
];
// export const dynamic = "force-dynamic" // export const dynamic = "force-dynamic"
// export const revalidate = 0 // export const revalidate = 0
@@ -97,6 +56,64 @@ export default function HomeView({ user }: { user: USER_PROFILE }) {
const router = useRouter(); const router = useRouter();
const [stateUser, setStateUser] = useState(user); const [stateUser, setStateUser] = useState(user);
const listHalaman = [
{
id: 1,
name: "Forums",
icon: <IconMessages size={50} />,
link: ""
},
{
id: 2,
name: "Project Collaboration",
icon: <IconAffiliate size={50} />,
link: ""
},
{
id: 3,
name: "Voting",
icon: <IconPackageImport size={50} />,
link: ""
},
{
id: 4,
name: "Event",
icon: <IconPresentation size={50} />,
link: ""
},
{
id: 5,
name: "Crowd Funding",
icon: <IconHeartHandshake size={50} />,
link: `/dev/crowd/splash`
},
{
id: 6,
name: "Marketplace",
icon: <IconShoppingBag size={50} />,
link: ""
},
{
id: 7,
name: "Job Vacancy",
icon: <IconBriefcase size={50} />,
link: ""
},
{
id: 8,
name: "Business Maps",
icon: <IconMap2 size={50} />,
link: ""
},
];
return ( return (
<> <>
<Box> <Box>
@@ -134,7 +151,13 @@ export default function HomeView({ user }: { user: USER_PROFILE }) {
key={e.id} key={e.id}
h={100} h={100}
withBorder withBorder
onClick={() => toast(e.name)} onClick={() => {
if(e.link === ""){
toast(e.name)
} else {
return router.push(e.link)
}
}}
> >
<Flex <Flex
justify={"center"} justify={"center"}

View File

@@ -0,0 +1,23 @@
"use client";
import HeaderTamplate from "@/app_modules/component/header_tamplate";
import { AppShell } from "@mantine/core";
import React from "react";
export default function InvestasiCreateLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<AppShell
header={
<HeaderTamplate route="/dev/investasi/main" title="Investasi Baru" />
}
>
{children}
</AppShell>
</>
);
}

View File

@@ -0,0 +1,209 @@
"use client";
import { ApiHipmi } from "@/app/lib/api";
import { Warna } from "@/app/lib/warna";
import { MODEL_ALL_MASTER } from "@/app_modules/models/model_AllMaster";
import {
AspectRatio,
Box,
Button,
Center,
FileButton,
Group,
Image,
Select,
TextInput,
} from "@mantine/core";
import { IconCamera } from "@tabler/icons-react";
import _ from "lodash";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { funCreateInvestasi } from "../fun/fun_create_investasi";
import toast from "react-simple-toasts";
export default function InvestasiCreate({
id,
pencarianInvestor,
periodeDeviden,
pembagianDeviden,
}: {
id: string;
pencarianInvestor: MODEL_ALL_MASTER[];
periodeDeviden: MODEL_ALL_MASTER[];
pembagianDeviden: MODEL_ALL_MASTER[];
}) {
const router = useRouter();
const [fl, setFl] = useState<File | null>(null);
const [img, setImg] = useState<any | null>();
const [value, setValue] = useState({
title: "",
targetDana: "",
hargaLembar: "",
totalLembar: "",
roi: "",
pencarianInvestorId: "",
periodeDevidenId: "",
pembagianDevidenId: "",
});
async function onSubmit() {
const body = {
authorId: id,
title: value.title,
targetDana: value.targetDana,
hargaLembar: value.hargaLembar,
totalLembar: value.totalLembar,
roi: value.roi,
masterPeriodeDevidenId: value.periodeDevidenId,
masterPembagianDevidenId: value.pembagianDevidenId,
masterPencarianInvestorId: value.pencarianInvestorId,
};
if (_.values(body).includes("")) return toast("Lengkapi data");
if (!fl) return toast("File Kosong");
const fd = new FormData();
fd.append("file", fl);
await funCreateInvestasi(fd, body as any).then((res) => {
if (res.status === 201) {
toast(res.message);
return router.push("/dev/investasi/main")
} else {
return toast(res.message);
}
});
}
return (
<>
<Box>
<AspectRatio ratio={16 / 9}>
{img ? (
<Image alt="" src={img} />
) : (
<Image alt="" src={"/aset/no-img.png"} />
)}
</AspectRatio>
<Group position="center" mt={"md"}>
<FileButton
onChange={async (files: any) => {
const buffer = URL.createObjectURL(
new Blob([new Uint8Array(await files.arrayBuffer())])
);
setImg(buffer);
setFl(files);
}}
accept="image/png,image/jpeg"
>
{(props) => (
<Button
{...props}
w={350}
radius={50}
// bg={Warna.biru}
// onClick={() => router.push("/dev/investasi/upload")}
>
<IconCamera />
</Button>
)}
</FileButton>
</Group>
<Center>
<Box mt={"md"} w={350}>
<TextInput
label="Judul Proyek"
onChange={(val) => {
setValue({
...value,
title: val.target.value,
});
}}
/>
<TextInput
label="Dana Dibutuhan"
type="number"
onChange={(val) => {
setValue({
...value,
targetDana: val.target.value,
});
}}
/>
<TextInput
label="Harga Per Lember"
type="number"
onChange={(val) => {
setValue({
...value,
hargaLembar: val.target.value,
});
}}
/>
<TextInput
label="Total Lembar"
type="number"
onChange={(val) => {
setValue({
...value,
totalLembar: val.target.value,
});
}}
/>
<TextInput
label="Rasio Keuntungan / ROI"
type="number"
onChange={(val) => {
setValue({
...value,
roi: val.target.value,
});
}}
/>
<Select
label="Pencarian Investor"
data={pencarianInvestor.map((e) => ({
value: e.id,
label: e.name,
}))}
onChange={(val) => {
setValue({
...(value as any),
pencarianInvestorId: val,
});
}}
/>
<Select
label="Periode Deviden"
data={periodeDeviden.map((e) => ({ value: e.id, label: e.name }))}
onChange={(val) => {
setValue({
...(value as any),
periodeDevidenId: val,
});
}}
/>
<Select
label="Pembagian Deviden"
data={pembagianDeviden.map((e) => ({
value: e.id,
label: e.name,
}))}
onChange={(val) => {
setValue({
...(value as any),
pembagianDevidenId: val,
});
}}
/>
</Box>
</Center>
<Center my={"lg"}>
<Button w={200} radius={50} onClick={() => onSubmit()}>
Simpan
</Button>
</Center>
</Box>
</>
);
}

View File

@@ -0,0 +1,74 @@
"use server";
import prisma from "@/app/lib/prisma";
import _ from "lodash";
import { v4 } from "uuid";
import fs from "fs";
import { INVESTASI } from "@/app_modules/models/investasi";
import { revalidatePath } from "next/cache";
export async function funCreateInvestasi(formData: FormData, data: INVESTASI) {
const file: any = formData.get("file");
// console.log(file)
const fName = file.name;
const fExt = _.lowerCase(file.name.split(".").pop());
const fRandomName = v4(fName) + "." + fExt;
const upload = await prisma.images.create({
data: {
url: fRandomName,
},
select: {
id: true,
url: true,
},
});
if (!upload)
return {
status: 400,
message: "File Kosong",
};
// if (upload) {
// await prisma.investasi.update({
// where: {
// authorId: data.authorId,
// },
// data: {
// imagesId: upload.id,
// },
// });
// }
const upFolder = Buffer.from(await file.arrayBuffer());
fs.writeFileSync(`./public/investasi/${upload.url}`, upFolder);
const createInvest = await prisma.investasi.create({
data: {
authorId: data.authorId,
title: data.title,
targetDana: data.targetDana,
hargaLembar: data.hargaLembar,
totalLembar: data.totalLembar,
roi: data.roi,
masterPembagianDevidenId: data.masterPembagianDevidenId,
masterPeriodeDevidenId: data.masterPeriodeDevidenId,
masterPencarianInvestorId: data.masterPencarianInvestorId,
imagesId: upload.id,
},
});
if (!createInvest)
return {
status: 400,
message: "Gagal Disimpan",
};
revalidatePath("/dev/investasi/main");
return {
status: 201,
message: "Berhasil Disimpan",
};
}

View File

@@ -0,0 +1,13 @@
"use server";
import prisma from "@/app/lib/prisma";
export async function getListAllInvestasi() {
const data = await prisma.investasi.findMany({
orderBy: {
createdAt: "desc"
}
});
return data;
}

View File

@@ -0,0 +1,15 @@
"use server";
import prisma from "@/app/lib/prisma";
export default async function getPembagianDeviden() {
const data = await prisma.masterPembagianDeviden.findMany({
select: {
id: true,
name: true,
active: true,
},
});
return data;
}

View File

@@ -0,0 +1,15 @@
"use server";
import prisma from "@/app/lib/prisma";
export default async function getPencarianInvestor() {
const data = await prisma.masterPencarianInvestor.findMany({
select: {
id: true,
name: true,
active: true
}
});
return data;
}

View File

@@ -0,0 +1,15 @@
"use server";
import prisma from "@/app/lib/prisma";
export default async function getPeriodeDeviden() {
const data = await prisma.masterPeriodeDeviden.findMany({
select: {
id: true,
name: true,
active: true,
},
});
return data;
}

View File

@@ -0,0 +1,15 @@
import MainInvestasi from "./main/view";
import LayoutMainInvestasi from "./main/layout";
import InvestasiCreate from "./create/view";
import InvestasiCreateLayout from "./create/layout";
import UploadGambarInvestasi from "./upload/view";
import LayoutUploadGambarInvestasi from "./upload/layout";
export {
MainInvestasi,
LayoutMainInvestasi,
InvestasiCreate,
InvestasiCreateLayout,
UploadGambarInvestasi,
LayoutUploadGambarInvestasi,
};

View File

@@ -0,0 +1,69 @@
"use client";
import HeaderTamplate from "@/app_modules/component/header_tamplate";
import {
ActionIcon,
AppShell,
Center,
Flex,
Footer,
Grid,
Group,
Stack,
Text,
} from "@mantine/core";
import {
IconChartHistogram,
IconChartPieFilled,
IconPencilPlus,
} from "@tabler/icons-react";
import React from "react";
export default function LayoutMainInvestasi({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<AppShell
header={
<HeaderTamplate
route="/dev/crowd/main"
title="Investasi"
icon={<IconPencilPlus />}
route2={"/dev/investasi/create"}
/>
}
footer={
<Footer height={70} bg={"dark"}>
<Grid align="center" h={60} pt={"xs"}>
<Grid.Col span={6}>
<Center>
<Flex direction={"column"} align={"center"} w={"100%"}>
<ActionIcon variant="transparent">
<IconChartHistogram />
</ActionIcon>
<Text c={"white"}>Bursa</Text>
</Flex>
</Center>
</Grid.Col>
<Grid.Col span={6}>
<Center>
<Flex direction={"column"} align={"center"} w={"100%"}>
<ActionIcon variant="transparent">
<IconChartPieFilled />
</ActionIcon>
<Text c={"white"}>Portofolio</Text>
</Flex>
</Center>
</Grid.Col>
</Grid>
</Footer>
}
>
{children}
</AppShell>
</>
);
}

View File

@@ -0,0 +1,104 @@
"use client";
import { ApiHipmi } from "@/app/lib/api";
import { INVESTASI } from "@/app_modules/models/investasi";
import { MODEL_ALL_MASTER } from "@/app_modules/models/model_AllMaster";
import {
AspectRatio,
Box,
Button,
Card,
CardSection,
Divider,
Grid,
Group,
Image,
Paper,
Slider,
Stack,
Text,
Title,
} from "@mantine/core";
import { useRouter } from "next/navigation";
export default function MainInvestasi({
listData,
pencarianInvestor,
periodeDeviden,
pembagianDeviden,
}: {
listData: INVESTASI[];
pencarianInvestor: MODEL_ALL_MASTER[];
periodeDeviden: MODEL_ALL_MASTER[];
pembagianDeviden: MODEL_ALL_MASTER[];
}) {
const router = useRouter();
return (
<>
<pre>{/* {JSON.stringify(listData, null, 2)} */}</pre>
{listData.map((e) => (
<Card key={e.id} p={"md"} withBorder mb={"lg"}>
<CardSection p={"xs"}>
<AspectRatio ratio={16 / 9}>
{e.imagesId ? (
<Image alt="" src={`/api/investasi/gambar/${e.imagesId}`} />
) : (
<Image alt="" src={"/aset/no-img.png"} />
)}
</AspectRatio>
</CardSection>
<CardSection p={"lg"}>
<Box mt={"md"}>
<Slider
size={10}
labelAlwaysOn
marks={[
// { value: 25, label: '25%' },
// { value: 50, label: '50%' },
// { value: 75, label: '75%' },
{ value: 100, label: "100%" },
]}
/>
<Title order={4}>{e.title}</Title>
</Box>
<Box mt={"md"}>
<Grid>
<Grid.Col span={6}>
<Stack>
<Box>
<Text>Dana Dibutuhkan</Text>
<Text>Rp. {e.targetDana}</Text>
</Box>
<Box>
<Text>Harga Per Lembar</Text>
<Text>Rp. {e.hargaLembar}</Text>
</Box>
</Stack>
</Grid.Col>
<Grid.Col span={6}>
<Stack>
<Box>
<Text>ROI</Text>
<Text>{e.roi}%</Text>
</Box>
<Box>
<Text>Total Lembar</Text>
<Text>{e.totalLembar}</Text>
</Box>
</Stack>
</Grid.Col>
</Grid>
</Box>
</CardSection>
<Divider />
<CardSection p={"md"}>
<Group position="right">
<Text>Selesai</Text>
</Group>
</CardSection>
</Card>
))}
</>
);
}

View File

@@ -0,0 +1,19 @@
"use client";
import HeaderTamplate from "@/app_modules/component/header_tamplate";
import { AppShell } from "@mantine/core";
import React from "react";
export default function LayoutUploadGambarInvestasi({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<AppShell header={<HeaderTamplate title="Upload Gambar Investasi" />}>
{children}
</AppShell>
</>
);
}

View File

@@ -0,0 +1,39 @@
"use client";
import { Warna } from "@/app/lib/warna";
import {
AspectRatio,
Button,
Center,
Divider,
FileButton,
Group,
Image,
Paper,
Text,
Title,
} from "@mantine/core";
import { useState } from "react";
export default function UploadGambarInvestasi() {
const [img, setImg] = useState<any | null>();
return (
<>
<Group position="center">
<FileButton
onChange={async (files : any) => {
const buffer = URL.createObjectURL(
new Blob([new Uint8Array( await files.arrayBuffer())])
);
setImg(buffer);
}}
accept="image/png,image/jpeg"
>
{(props) => <Button {...props}>Upload image</Button>}
</FileButton>
</Group>
{img && <Image alt="" src={img}/>}
</>
);
}

View File

@@ -0,0 +1,13 @@
export interface INVESTASI {
id: string
authorId: string;
title: string;
targetDana: string;
hargaLembar: string;
totalLembar: string;
roi: string;
masterPeriodeDevidenId: string;
masterPembagianDevidenId: string;
masterPencarianInvestorId: string;
imagesId: string
}

View File

@@ -0,0 +1,5 @@
export interface MODEL_ALL_MASTER {
id: string,
name: string,
active: boolean,
}

View File

@@ -0,0 +1,14 @@
[
{
"id": 1,
"name": "3 bulan"
},
{
"id": 2,
"name": "6 bulan"
},
{
"id": 3,
"name": "1 tahun"
}
]

View File

@@ -0,0 +1,19 @@
[
{
"id": 1,
"name": "30 hari"
},
{
"id": 2,
"name": "60 hari"
},
{
"id": 3,
"name": "90 hari"
},
{
"id": 4,
"name": "120 hari"
}
]

View File

@@ -0,0 +1,10 @@
[
{
"id": 1,
"name": "Selamanya"
},
{
"id": 2,
"name": "Satu tahun"
}
]

View File

@@ -287,6 +287,13 @@
react-remove-scroll "^2.5.5" react-remove-scroll "^2.5.5"
react-textarea-autosize "8.3.4" react-textarea-autosize "8.3.4"
"@mantine/dropzone@^7.1.3":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@mantine/dropzone/-/dropzone-7.1.3.tgz#02851a45fc7278b63f8b628f0fc35f75a6650563"
integrity sha512-7MnXTtlQlL9Q0GluRUKv8AWFDfPv7jv74K+F08Y5bKiQHYR8OzIW4Pc+Vk4Ea9I8/EzoKLWarF0Bn2KkgoO4Lw==
dependencies:
react-dropzone-esm "15.0.1"
"@mantine/hooks@^6.0.17": "@mantine/hooks@^6.0.17":
version "6.0.17" version "6.0.17"
resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-6.0.17.tgz#dc942c87e6dcfa14a70e4e4a162c22eeb4ff3724" resolved "https://registry.yarnpkg.com/@mantine/hooks/-/hooks-6.0.17.tgz#dc942c87e6dcfa14a70e4e4a162c22eeb4ff3724"
@@ -2913,6 +2920,13 @@ react-dom@18.2.0:
loose-envify "^1.1.0" loose-envify "^1.1.0"
scheduler "^0.23.0" scheduler "^0.23.0"
react-dropzone-esm@15.0.1:
version "15.0.1"
resolved "https://registry.yarnpkg.com/react-dropzone-esm/-/react-dropzone-esm-15.0.1.tgz#8c689638aaa9feb5b2a429dd565acfa6792263e5"
integrity sha512-RdeGpqwHnoV/IlDFpQji7t7pTtlC2O1i/Br0LWkRZ9hYtLyce814S71h5NolnCZXsIN5wrZId6+8eQj2EBnEzg==
dependencies:
prop-types "^15.8.1"
react-is@^16.13.1, react-is@^16.7.0: react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1" version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"