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

@@ -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 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 revalidate = 0
@@ -97,6 +56,64 @@ export default function HomeView({ user }: { user: USER_PROFILE }) {
const router = useRouter();
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 (
<>
<Box>
@@ -134,7 +151,13 @@ export default function HomeView({ user }: { user: USER_PROFILE }) {
key={e.id}
h={100}
withBorder
onClick={() => toast(e.name)}
onClick={() => {
if(e.link === ""){
toast(e.name)
} else {
return router.push(e.link)
}
}}
>
<Flex
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,
}