Merge pull request #275 from bipproduction/Nico/04-feb-25

API Table Investasi Admin
This commit is contained in:
Bagasbanuna02
2025-02-05 10:36:22 +08:00
committed by GitHub
40 changed files with 1737 additions and 378 deletions

View File

@@ -0,0 +1,164 @@
import { prisma } from "@/app/lib";
import _ from "lodash";
import moment from "moment";
import { NextResponse } from "next/server";
export async function GET(request: Request, { params }: {
params: { status: string }
}
) {
const method = request.method;
if (method !== "GET") {
return NextResponse.json({
success: false,
message: "Method not allowed",
},
{ status: 405 }
)
}
const { status } = params;
const { searchParams } = new URL(request.url);
const search = searchParams.get("search");
const page = searchParams.get("page");
const takeData = 10
const skipData = Number(page) * takeData - takeData;
console.log("Ini Status", status);
console.log("Ini Page", page)
try {
let fixData;
const fixStatus = _.startCase(status);
if (!page && !search) {
fixData = await prisma.investasi.findMany({
orderBy: {
updatedAt: "desc",
},
where: {
active: true,
MasterStatusInvestasi: {
name: fixStatus
},
},
include: {
author: {
select: {
id: true,
username: true,
Profile: {
select: {
name: true,
},
},
},
},
MasterStatusInvestasi: true,
},
});
} else if (!page && search) {
fixData = await prisma.investasi.findMany({
orderBy: {
updatedAt: "desc",
},
where: {
active: true,
MasterStatusInvestasi: {
name: fixStatus
},
title: {
contains: search,
mode: "insensitive",
},
},
include: {
author: {
select: {
id: true,
username: true,
Profile: {
select: {
name: true,
},
},
},
},
MasterStatusInvestasi: true,
},
});
} else if (page && !search) {
const data = await prisma.investasi.findMany({
take: takeData,
skip: skipData,
orderBy: [
{
countDown: "desc",
},
],
where: {
active: true,
MasterStatusInvestasi: {
name: fixStatus
}
},
include: {
MasterStatusInvestasi: true,
BeritaInvestasi: true,
DokumenInvestasi: true,
ProspektusInvestasi: true,
MasterPembagianDeviden: true,
MasterPencarianInvestor: true,
MasterPeriodeDeviden: true,
author: true,
Investasi_Invoice: {
where: {
statusInvoiceId: "2",
},
},
},
});
const nCount = await prisma.investasi.count({
where: {
active: true,
MasterStatusInvestasi: {
name: fixStatus
}
},
});
console.log("data >", data)
fixData = {
data: data,
nPage: _.ceil(nCount / takeData),
};
}
return NextResponse.json({
success: true,
message: "Success",
data: fixData,
},
{ status: 200 }
)
} catch (error) {
console.log(error)
return NextResponse.json({
success: false,
message: "Failed",
reason: (error as Error).message,
},
{ status: 500 }
)
}
}

View File

@@ -2,10 +2,10 @@ import { Admin_TableRejectInvestasi } from "@/app_modules/admin/investasi";
import { adminInvestasi_funGetAllReject } from "@/app_modules/admin/investasi/fun/get/get_all_reject";
export default async function Page() {
const dataInvestsi = await adminInvestasi_funGetAllReject({page: 1});
// const dataInvestsi = await adminInvestasi_funGetAllReject({page: 1});
return (
<>
<Admin_TableRejectInvestasi dataInvestsi={dataInvestsi as any} />
<Admin_TableRejectInvestasi />
</>
);
}

View File

@@ -2,10 +2,10 @@ import { Admin_TableReviewInvestasi } from "@/app_modules/admin/investasi";
import { adminInvestasi_funGetAllReview } from "@/app_modules/admin/investasi/fun/get/get_all_review";
export default async function Page() {
const dataInvestsi = await adminInvestasi_funGetAllReview({ page: 1 });
// const dataInvestsi = await adminInvestasi_funGetAllReview({ page: 1 });
return (
<>
<Admin_TableReviewInvestasi dataInvestsi={dataInvestsi as any} />
<Admin_TableReviewInvestasi />
</>
);
}

View File

@@ -49,6 +49,7 @@ import adminNotifikasi_funCreateToUser from "../../notifikasi/fun/create/fun_cre
import { adminEvent_funGetListReview } from "../fun";
import { AdminEvent_funEditStatusPublishById } from "../fun/edit/fun_edit_status_publish_by_id";
import { AdminEvent_funEditCatatanById } from "../fun/edit/fun_edit_status_reject_by_id";
import { AdminColor } from "@/app_modules/_global/color/color_pallet";
export default function AdminEvent_ComponentTableReview() {
const [data, setData] = useState<MODEL_EVENT[] | null>(null);
@@ -261,28 +262,28 @@ export default function AdminEvent_ComponentTableReview() {
return data.map((e, i) => (
<tr key={i}>
<td>
<Center w={200}>
<Center c={AdminColor.white} w={200}>
<Text>{e?.Author?.username}</Text>
</Center>
</td>
<td>
<Center w={200}>
<Center c={AdminColor.white} w={200}>
<Text lineClamp={2}>{e.title}</Text>
</Center>
</td>
<td>
<Center w={200}>
<Center c={AdminColor.white} w={200}>
<Text>{e.lokasi}</Text>
</Center>
</td>
<td>
<Center w={200}>
<Center c={AdminColor.white} w={200}>
<Text>{e.EventMaster_TipeAcara.name}</Text>
</Center>
</td>
<td>
<Center w={200}>
<Center c={AdminColor.white} w={200}>
<Text align="center">
{new Intl.DateTimeFormat("id-ID", {
dateStyle: "full",
@@ -297,7 +298,7 @@ export default function AdminEvent_ComponentTableReview() {
</Center>
</td>
<td>
<Center w={200}>
<Center c={AdminColor.white} w={200}>
<Text align="center">
{new Intl.DateTimeFormat("id-ID", {
dateStyle: "full",
@@ -313,7 +314,7 @@ export default function AdminEvent_ComponentTableReview() {
</td>
<td>
<Center w={400}>
<Center c={AdminColor.white} w={400}>
<Spoiler
hideLabel="sembunyikan"
maxHeight={50}
@@ -371,7 +372,7 @@ export default function AdminEvent_ComponentTableReview() {
<Stack spacing={"xs"} h={"100%"}>
<ComponentAdminGlobal_TitlePage
name="Review"
color={"orange.5"}
color={AdminColor.softBlue}
component={
<TextInput
disabled={!data}
@@ -387,7 +388,7 @@ export default function AdminEvent_ComponentTableReview() {
{!data ? (
<CustomSkeleton height={"80vh"} width="100%" />
) : (
<Paper p={"md"} withBorder shadow="lg" h={"80vh"}>
<Paper p={"md"} bg={AdminColor.softBlue} h={"80vh"}>
{isShowReload && (
<Affix position={{ top: rem(200) }} w={"100%"}>
<Center>
@@ -416,35 +417,33 @@ export default function AdminEvent_ComponentTableReview() {
horizontalSpacing={"md"}
p={"md"}
w={1500}
striped
highlightOnHover
>
<thead>
<tr>
<th>
<Center>Username</Center>
<Center c={AdminColor.white}>Username</Center>
</th>
<th>
<Center>Judul</Center>
<Center c={AdminColor.white}>Judul</Center>
</th>
<th>
<Center>Lokasi</Center>
<Center c={AdminColor.white}>Lokasi</Center>
</th>
<th>
<Center>Tipe Acara</Center>
<Center c={AdminColor.white}>Tipe Acara</Center>
</th>
<th>
<Center>Tanggal & Waktu Mulai</Center>
<Center c={AdminColor.white}>Tanggal & Waktu Mulai</Center>
</th>
<th>
<Center>Tanggal & Waktu Selesai</Center>
<Center c={AdminColor.white}>Tanggal & Waktu Selesai</Center>
</th>
<th>
<Center>Deskripsi</Center>
<Center c={AdminColor.white}>Deskripsi</Center>
</th>
<th>
<Center>Aksi</Center>
<Center c={AdminColor.white}>Aksi</Center>
</th>
</tr>
</thead>

View File

@@ -137,20 +137,20 @@ function TableStatus() {
return data.map((e, i) => (
<tr key={i}>
<td>
<Center w={200}>{e?.Author?.username}</Center>
<Center c={AdminColor.white} w={200}>{e?.Author?.username}</Center>
</td>
<td>
<Center w={200}>{e.title}</Center>
<Center c={AdminColor.white} w={200}>{e.title}</Center>
</td>
<td>
<Center w={200}>{e.lokasi}</Center>
<Center c={AdminColor.white} w={200}>{e.lokasi}</Center>
</td>
<td>
<Center w={200}>{e.EventMaster_TipeAcara.name}</Center>
<Center c={AdminColor.white} w={200}>{e.EventMaster_TipeAcara.name}</Center>
</td>
<td>
<Center w={200}>
<Center c={AdminColor.white} w={200}>
<Text align="center">
{new Intl.DateTimeFormat("id-ID", {
dateStyle: "full",
@@ -165,7 +165,7 @@ function TableStatus() {
</Center>
</td>
<td>
<Center w={200}>
<Center c={AdminColor.white} w={200}>
<Text align="center">
{new Intl.DateTimeFormat("id-ID", {
dateStyle: "full",
@@ -181,7 +181,7 @@ function TableStatus() {
</td>
<td>
<Center w={500}>
<Center c={AdminColor.white} w={500}>
<Spoiler
hideLabel="sembunyikan"
maxHeight={50}
@@ -193,7 +193,7 @@ function TableStatus() {
</td>
<td>
{" "}
<Center w={400}>
<Center c={AdminColor.white} w={400}>
<Spoiler
hideLabel="sembunyikan"
maxHeight={50}
@@ -227,7 +227,7 @@ function TableStatus() {
<Stack spacing={"xs"} h={"100%"}>
<ComponentAdminGlobal_TitlePage
name="Reject"
color={"red.5"}
color={AdminColor.softBlue}
component={
<TextInput
disabled={!data}
@@ -244,44 +244,43 @@ function TableStatus() {
{!data ? (
<CustomSkeleton height={"80vh"} width="100%" />
) : (
<Paper p={"md"} withBorder shadow="lg" h={"80vh"}>
<Paper p={"md"} bg={AdminColor.softBlue} h={"80vh"}>
<ScrollArea w={"100%"} h={"90%"}>
<Table
verticalSpacing={"md"}
horizontalSpacing={"md"}
p={"md"}
w={1500}
striped
highlightOnHover
>
<thead>
<tr>
<th>
<Center>Username</Center>
<Center c={AdminColor.white}>Username</Center>
</th>
<th>
<Center>Judul</Center>
<Center c={AdminColor.white}>Judul</Center>
</th>
<th>
<Center>Lokasi</Center>
<Center c={AdminColor.white}>Lokasi</Center>
</th>
<th>
<Center>Tipe Acara</Center>
<Center c={AdminColor.white}>Tipe Acara</Center>
</th>
<th>
<Center>Tanggal & Waktu Mulai</Center>
<Center c={AdminColor.white}>Tanggal & Waktu Mulai</Center>
</th>
<th>
<Center>Tanggal & Waktu Selesai</Center>
<Center c={AdminColor.white}>Tanggal & Waktu Selesai</Center>
</th>
<th>
<Center>Cacatan</Center>
<Center c={AdminColor.white}>Cacatan</Center>
</th>
<th>
<Center>Deskripsi</Center>
<Center c={AdminColor.white}>Deskripsi</Center>
</th>
<th>
<Center>Aksi</Center>
<Center c={AdminColor.white}>Aksi</Center>
</th>
</tr>
</thead>

View File

@@ -0,0 +1,51 @@
export {
apiGetAdminInvestasiCountDashboard,
apiGetAdminInvestasiByStatus,
}
const apiGetAdminInvestasiCountDashboard = async ({ name }:
{ name: "Publish" | "Review" | "Reject" }) => {
const { token } = await fetch("/api/get-cookie").then((res) => res.json());
if (!token) return await token.json().catch(() => null);
const response = await fetch(`/api/admin/investasi/dashboard/${name}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
})
return await response.json().catch(() => null);
};
const apiGetAdminInvestasiByStatus = async ({ status, page, search }: {
status: "Publish" | "Review" | "Reject",
page: string,
search: string
}) => {
console.log("dgsdg")
const { token } = await fetch("/api/get-cookie").then((res) => res.json());
if (!token) return await token.json().catch(() => null);
console.log("Ini token", token)
console.log("Ini Page", page)
console.log("Ini Search", search)
const isPage = page ? `?page=${page}` : "";
const isSearch = search ? `&search=${search}` : "";
const response = await fetch(`/api/admin/investasi/${status}${isPage}${isSearch}`, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
})
console.log("Ini response", response)
return await response.json().catch(() => null);
}

View File

@@ -1,22 +0,0 @@
const apiGetInvestasiCountDashboard = async ({ name }:
{ name: "Publish" | "Review" | "Reject" }) => {
const { token } = await fetch("/api/get-cookie").then((res) => res.json());
if (!token) return await token.json().catch(() => null);
const response = await fetch(`/api/admin/investasi/dashboard/${name}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
})
return await response.json().catch(() => null);
};
export default apiGetInvestasiCountDashboard;

View File

@@ -25,6 +25,10 @@ import { adminInvestasi_funGetAllPublish } from "../fun/get/get_all_publish";
import { ComponentAdminGlobal_TitlePage } from "../../_admin_global/_component";
import { MainColor } from "@/app_modules/_global/color";
import { AccentColor, AdminColor } from "@/app_modules/_global/color/color_pallet";
import { apiGetAdminInvestasiByStatus } from "../_lib/api_fetch_admin_investasi";
import { useShallowEffect } from "@mantine/hooks";
import { clientLogger } from "@/util/clientLogger";
import CustomSkeleton from "@/app_modules/components/CustomSkeleton";
export default function Admin_TablePublishInvestasi({
dataInvestsi,
@@ -50,83 +54,116 @@ function TableView({ listData }: { listData: any }) {
const [isSearch, setSearch] = useState("");
const [isLoading, setLoading] = useState(false);
const [idData, setIdData] = useState("");
const [origin, setOrigin] = useState("");
async function onSearch(s: string) {
setSearch(s);
useShallowEffect(() => {
if (typeof window !== "undefined") {
setOrigin(window.location.origin);
}
}, [])
useShallowEffect(() => {
const loadInitialData = async () => {
try {
const response = await apiGetAdminInvestasiByStatus({
status: "Publish",
page: `${activePage}`,
search: isSearch,
});
if (response?.success && response?.data?.data) {
setData(response.data.data);
setNPage(response.data.nPage || 1);
} else {
console.error("Invalid data format received:", response);
setData([]);
}
} catch (error) {
clientLogger.error("Error get data table publish", error);
setData([]);
}
};
loadInitialData();
}, [activePage, isSearch]);
const onSearch = async (searchTerm: string) => {
setSearch(searchTerm);
setActivePage(1);
const loadData = await adminInvestasi_funGetAllPublish({
page: 1,
search: s,
});
setData(loadData.data as any);
setNPage(loadData.nPage);
}
async function onPageClick(p: any) {
setActivePage(p);
const loadData = await adminInvestasi_funGetAllPublish({
search: isSearch,
page: p,
});
setData(loadData.data as any);
setNPage(loadData.nPage);
const onPageClick = (page: number) => {
setActivePage(page)
}
const tableBody = data.map((e) => (
<tr key={e.id}>
<td>
<Center w={200}>
<Text c={AccentColor.white} lineClamp={1}>{e.author.username}</Text>
</Center>
</td>
<td>
<Center w={400}>
<Text c={AccentColor.white} lineClamp={1}>{e.title}</Text>
</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>{_.toNumber(e.progress).toFixed(2)} %</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>
{new Intl.NumberFormat("id-ID", {
maximumFractionDigits: 10,
}).format(+e.sisaLembar)}
</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>
{new Intl.NumberFormat("id-ID", {
maximumFractionDigits: 10,
}).format(+e.totalLembar)}
</Center>
</td>
<td>
<Center w={200}>
<Text c={AccentColor.white} lineClamp={1}>{e.Investasi_Invoice.length}</Text>
</Center>
</td>
<td>
<Center w={200}>
<Button
loading={isLoading && idData === e.id}
loaderPosition="center"
bg={"green"}
color="green"
radius={"xl"}
leftIcon={<IconEyeCheck size={20} />}
onClick={() => {
setIdData(e.id);
setLoading(true);
router.push(RouterAdminInvestasi.detail_publish + `${e.id}`);
}}
>
Detail
</Button>
</Center>
</td>
</tr>
));
const renderTableBody = () => {
if(!Array.isArray(data) || data.length === 0) {
return (
<tr>
<td colSpan={12}>
<Center>
<Text color={"gray"}>Tidak ada data</Text>
</Center>
</td>
</tr>
);
}
return data.map((e, i) => (
<tr key={i}>
<td>
<Center w={200}>
<Text c={AccentColor.white} lineClamp={1}>{e.author.username}</Text>
</Center>
</td>
<td>
<Center w={400}>
<Text c={AccentColor.white} lineClamp={1}>{e.title}</Text>
</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>{_.toNumber(e.progress).toFixed(2)} %</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>
{new Intl.NumberFormat("id-ID", {
maximumFractionDigits: 10,
}).format(+e.sisaLembar)}
</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>
{new Intl.NumberFormat("id-ID", {
maximumFractionDigits: 10,
}).format(+e.totalLembar)}
</Center>
</td>
<td>
<Center w={200}>
<Text c={AccentColor.white} lineClamp={1}>{e.Investasi_Invoice.length}</Text>
</Center>
</td>
<td>
<Center w={200}>
<Button
loading={isLoading && idData === e.id}
loaderPosition="center"
bg={"green"}
color="green"
radius={"xl"}
leftIcon={<IconEyeCheck size={20} />}
onClick={() => {
setIdData(e.id);
setLoading(true);
router.push(RouterAdminInvestasi.detail_publish + `${e.id}`);
}}
>
Detail
</Button>
</Center>
</td>
</tr>
));
}
return (
<>
@@ -164,8 +201,8 @@ function TableView({ listData }: { listData: any }) {
/>
</Group> */}
{_.isEmpty(data) ? (
<ComponentAdminGlobal_IsEmptyData />
{!data ? (
<CustomSkeleton height={"80vh"} width={"100%"} />
) : (
<Paper bg={AdminColor.softBlue} p={"md"} shadow="lg" h={"80vh"}>
<ScrollArea w={"100%"} h={"90%"} offsetScrollbars>
@@ -203,7 +240,7 @@ function TableView({ listData }: { listData: any }) {
</th>
</tr>
</thead>
<tbody>{tableBody}</tbody>
<tbody>{renderTableBody()}</tbody>
</Table>
</ScrollArea>
<Center mt={"xl"}>

View File

@@ -31,162 +31,186 @@ import { RouterAdminInvestasi } from "@/app/lib/router_admin/router_admin_invest
import { ComponentAdminGlobal_TitlePage } from "../../_admin_global/_component";
import { MainColor } from "@/app_modules/_global/color";
import { AccentColor, AdminColor } from "@/app_modules/_global/color/color_pallet";
import { useShallowEffect } from "@mantine/hooks";
import { clientLogger } from "@/util/clientLogger";
import { apiGetAdminInvestasiByStatus } from "../_lib/api_fetch_admin_investasi";
import CustomSkeleton from "@/app_modules/components/CustomSkeleton";
export default function Admin_TableRejectInvestasi({
dataInvestsi,
}: {
dataInvestsi: any[];
}) {
const [investasi, setInvestasi] = useState(dataInvestsi);
const router = useRouter();
export default function Admin_TableRejectInvestasi() {
return (
<>
<Stack>
<ComponentAdminGlobal_HeaderTamplate name="Investasi" />
<TableView listData={dataInvestsi} />
<TableView/>
</Stack>
</>
);
const tableBody = investasi.map((e) =>
e.MasterStatusInvestasi.id === "4" ? (
<tr key={e.id}>
<td>
<Group position="left">
<Avatar variant="outline" radius={"xl"} />
<Text>{e.author.username}</Text>
</Group>
</td>
<td>{_.capitalize(e.title)}</td>
<td>{e.catatan}</td>
<td>
<Center>
<Tooltip label="Konfirmasi" withArrow position="bottom">
<ActionIcon
variant="transparent"
onClick={() =>
router.push(RouterAdminInvestasi_OLD.konfirmasi + `${e.id}`)
}
>
<IconEdit color="green" />
</ActionIcon>
</Tooltip>
</Center>
</td>
</tr>
) : (
""
)
);
// const tableBody = investasi.map((e) =>
// e.MasterStatusInvestasi.id === "4" ? (
// <tr key={e.id}>
// <td>
// <Group position="left">
// <Avatar variant="outline" radius={"xl"} />
// <Text>{e.author.username}</Text>
// </Group>
// </td>
// <td>{_.capitalize(e.title)}</td>
// <td>{e.catatan}</td>
// <td>
// <Center>
// <Tooltip label="Konfirmasi" withArrow position="bottom">
// <ActionIcon
// variant="transparent"
// onClick={() =>
// router.push(RouterAdminInvestasi_OLD.konfirmasi + `${e.id}`)
// }
// >
// <IconEdit color="green" />
// </ActionIcon>
// </Tooltip>
// </Center>
// </td>
// </tr>
// ) : (
// ""
// )
// );
return (
<>
<Stack>
<ActionIcon
variant="outline"
onClick={() => router.push(RouterAdminInvestasi_OLD.main_investasi)}
>
<IconChevronLeft />
</ActionIcon>
<Box>
<ScrollArea w={"100%"}>
<Badge color="red" variant="light" radius={0} size={"xl"}>
Reject
</Badge>
<Table
withBorder
highlightOnHover
verticalSpacing={"md"}
horizontalSpacing={"md"}
>
<thead>
<tr>
<th>Username</th>
<th>Nama Proyek Investasi</th>
<th>Catatan</th>
<th>
<Center>Aksi</Center>
</th>
</tr>
</thead>
<tbody>{tableBody}</tbody>
</Table>
</ScrollArea>
</Box>
</Stack>
</>
);
// return (
// <>
// <Stack>
// <ActionIcon
// variant="outline"
// onClick={() => router.push(RouterAdminInvestasi_OLD.main_investasi)}
// >
// <IconChevronLeft />
// </ActionIcon>
// <Box>
// <ScrollArea w={"100%"}>
// <Badge color="red" variant="light" radius={0} size={"xl"}>
// Reject
// </Badge>
// <Table
// withBorder
// highlightOnHover
// verticalSpacing={"md"}
// horizontalSpacing={"md"}
// >
// <thead>
// <tr>
// <th>Username</th>
// <th>Nama Proyek Investasi</th>
// <th>Catatan</th>
// <th>
// <Center>Aksi</Center>
// </th>
// </tr>
// </thead>
// <tbody>{tableBody}</tbody>
// </Table>
// </ScrollArea>
// </Box>
// </Stack>
// </>
// );
}
function TableView({ listData }: { listData: any }) {
function TableView() {
const router = useRouter();
const [data, setData] = useState<MODEL_INVESTASI[]>(listData.data);
const [nPage, setNPage] = useState(listData.nPage);
const [data, setData] = useState<MODEL_INVESTASI[] | null > (null);
const [nPage, setNPage] = useState(1);
const [activePage, setActivePage] = useState(1);
const [isSearch, setSearch] = useState("");
const [isLoading, setLoading] = useState(false);
const [idData, setIdData] = useState("");
async function onSearch(s: string) {
setSearch(s);
useShallowEffect(() => {
const loadInitialData = async () => {
try {
const response = await apiGetAdminInvestasiByStatus({
status: "Reject",
page: `${activePage}`,
search: isSearch,
});
if (response?.success && response?.data.data) {
setData(response.data.data);
setNPage(response.data.nPage || 1);
} else {
console.error("Invalid data format recieved:", response);
setData([]);
}
} catch (error) {
clientLogger.error("Error get data reject", error);
setData([]);
}
}
loadInitialData();
}, [activePage, isSearch]);
const onSearch = async (searchTerm: string) => {
setSearch(searchTerm);
setActivePage(1);
const loadData = await adminInvestasi_funGetAllReject({
page: 1,
search: s,
});
setData(loadData.data as any);
setNPage(loadData.nPage);
}
async function onPageClick(p: any) {
setActivePage(p);
const loadData = await adminInvestasi_funGetAllReject({
search: isSearch,
page: p,
});
setData(loadData.data as any);
setNPage(loadData.nPage);
async function onPageClick(page: number) {
setActivePage(page);
}
const tableBody = data.map((e) => (
<tr key={e.id}>
<td>
<Center w={200}>
<Text c={AccentColor.white} lineClamp={1}>{e.author.username}</Text>
</Center>
</td>
<td>
<Center w={400}>
<Text c={AccentColor.white} lineClamp={1}>{e.title}</Text>
</Center>
</td>
<td>
<Center w={400}>
<Text c={AccentColor.white} lineClamp={1}>{e.catatan}</Text>
</Center>
</td>
const renderTableBody = () => {
if (!Array.isArray(data) || data.length === 0) {
return (
<tr>
<td colSpan={12}>
<Center>
<Text color="gray">Tidak ada data</Text>
</Center>
</td>
</tr>
)
}
return data.map((e, i) => (
<tr key={i}>
<td>
<Center w={200}>
<Text c={AccentColor.white} lineClamp={1}>{e.author.username}</Text>
</Center>
</td>
<td>
<Center w={400}>
<Text c={AccentColor.white} lineClamp={1}>{e.title}</Text>
</Center>
</td>
<td>
<Center w={400}>
<Text c={AccentColor.white} lineClamp={1}>{e.catatan}</Text>
</Center>
</td>
<td>
<Center w={200}>
<Button
loading={isLoading && idData === e.id}
loaderPosition="center"
color="green"
leftIcon={<IconEyeCheck size={20}/>}
radius={"xl"}
onClick={() => {
setIdData(e.id);
setLoading(true);
router.push(RouterAdminInvestasi.detail_reject + `${e.id}`);
}}
>
Detail
</Button>
</Center>
</td>
</tr>
));
}
<td>
<Center w={200}>
<Button
loading={isLoading && idData === e.id}
loaderPosition="center"
color="green"
leftIcon={<IconEyeCheck size={20}/>}
radius={"xl"}
onClick={() => {
setIdData(e.id);
setLoading(true);
router.push(RouterAdminInvestasi.detail_reject + `${e.id}`);
}}
>
Detail
</Button>
</Center>
</td>
</tr>
));
return (
<>
@@ -224,8 +248,8 @@ function TableView({ listData }: { listData: any }) {
/>
</Group> */}
{_.isEmpty(data) ? (
<ComponentAdminGlobal_IsEmptyData />
{!data ? (
<CustomSkeleton height={"80vh"} width="100%" />
) : (
<Paper p={"md"} bg={AdminColor.softBlue} shadow="lg" h={"80vh"}>
<ScrollArea w={"100%"} h={"90%"} offsetScrollbars>
@@ -253,7 +277,7 @@ function TableView({ listData }: { listData: any }) {
</th>
</tr>
</thead>
<tbody>{tableBody}</tbody>
<tbody>{renderTableBody()}</tbody>
</Table>
</ScrollArea>
<Center mt={"xl"}>

View File

@@ -30,26 +30,25 @@ import ComponentAdminGlobal_TampilanRupiahDonasi from "../../_admin_global/tampi
import { adminInvestasi_funGetAllReview } from "../fun/get/get_all_review";
import { ComponentAdminGlobal_TitlePage } from "../../_admin_global/_component";
import { AdminColor } from "@/app_modules/_global/color/color_pallet";
import { apiGetAdminInvestasiByStatus } from "../_lib/api_fetch_admin_investasi";
import { clientLogger } from "@/util/clientLogger";
import CustomSkeleton from "@/app_modules/components/CustomSkeleton";
export default function Admin_TableReviewInvestasi({
dataInvestsi,
}: {
dataInvestsi: any[];
}) {
export default function Admin_TableReviewInvestasi() {
return (
<>
<Stack>
<ComponentAdminGlobal_HeaderTamplate name="Investasi" />
<TableView listData={dataInvestsi} />
<TableView />
</Stack>
</>
);
}
function TableView({ listData }: { listData: any }) {
function TableView() {
const router = useRouter();
const [data, setData] = useState<MODEL_INVESTASI[]>(listData.data);
const [nPage, setNPage] = useState(listData.nPage);
const [data, setData] = useState<MODEL_INVESTASI[] | null>(null);
const [nPage, setNPage] = useState<number>(1);
const [activePage, setActivePage] = useState(1);
const [isSearch, setSearch] = useState("");
const [isLoading, setLoading] = useState(false);
@@ -62,94 +61,108 @@ function TableView({ listData }: { listData: any }) {
const [isLoadingReload, setLoadingReload] = useState(false);
useShallowEffect(() => {
if (isAdminInvestasi_TriggerReview) {
setIsShowReload(false);
setIsAdminInvestasi_TriggerReview(false)
}
}, [isAdminInvestasi_TriggerReview]);
loadInitialData();
}, [activePage, isSearch]);
const loadInitialData = async () => {
try {
const response = await apiGetAdminInvestasiByStatus({
status: "Review",
page: `${activePage}`,
search: isSearch,
});
if (response?.success && response?.data?.data) {
setData(response.data.data)
setNPage(response.data.nPage || 1)
} else {
console.error("Invalid data format received:", response);
setData([]);
}
} catch (error) {
clientLogger.error("Error get data table review", error);
setData([]);
}
}
const onSearch = async (searchTerm: string) => {
setSearch(searchTerm);
setActivePage(1);
}
const onPageClick = (page: number) => {
setActivePage(page);
}
async function onLoadData() {
const loadData = await adminInvestasi_funGetAllReview({ page: 1 });
setData(loadData.data as any);
setNPage(loadData.nPage);
setLoadingReload(false);
loadInitialData();
setLoading(false);
setIsShowReload(false);
setIsAdminInvestasi_TriggerReview(false);
}
async function onSearch(s: string) {
setSearch(s);
setActivePage(1);
const loadData = await adminInvestasi_funGetAllReview({
page: 1,
search: s,
});
setData(loadData.data as any);
setNPage(loadData.nPage);
const renderTableBody = () => {
if (!Array.isArray(data) || data.length === 0) {
return (
<tr>
<td colSpan={12}>
<Center>
<Text color="gray">Tidak ada data</Text>
</Center>
</td>
</tr>
)
}
return data.map((e, i) => (
<tr key={i}>
<td>
<Center c={AccentColor.white} w={200}>
<Text lineClamp={1}>{e.author.username}</Text>
</Center>
</td>
<td>
<Center c={AccentColor.white} w={400}>
<Text lineClamp={1}>{e.title}</Text>
</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>
<Text lineClamp={1}>{e.roi} %</Text>
</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>
<ComponentAdminGlobal_TampilanRupiahDonasi
nominal={_.toNumber(e.targetDana)}
/>
</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>
<ComponentAdminGlobal_TampilanRupiahDonasi
nominal={_.toNumber(e.hargaLembar)}
/>
</Center>
</td>
<td>
<Center w={200}>
<Button
loading={isLoading && idData === e.id}
loaderPosition="center"
color="green"
leftIcon={<IconEyeCheck size={20} />}
radius={"xl"}
onClick={() => {
setIdData(e.id);
setLoading(true);
router.push(RouterAdminInvestasi_OLD.konfirmasi + `${e.id}`);
}}
>
Detail
</Button>
</Center>
</td>
</tr>
));
}
async function onPageClick(p: any) {
setActivePage(p);
const loadData = await adminInvestasi_funGetAllReview({
search: isSearch,
page: p,
});
setData(loadData.data as any);
setNPage(loadData.nPage);
}
const tableBody = data.map((e) => (
<tr key={e.id}>
<td>
<Center c={AccentColor.white} w={200}>
<Text lineClamp={1}>{e.author.username}</Text>
</Center>
</td>
<td>
<Center c={AccentColor.white} w={400}>
<Text lineClamp={1}>{e.title}</Text>
</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>
<Text lineClamp={1}>{e.roi} %</Text>
</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>
<ComponentAdminGlobal_TampilanRupiahDonasi
nominal={_.toNumber(e.targetDana)}
/>
</Center>
</td>
<td>
<Center c={AccentColor.white} w={200}>
<ComponentAdminGlobal_TampilanRupiahDonasi
nominal={_.toNumber(e.hargaLembar)}
/>
</Center>
</td>
<td>
<Center w={200}>
<Button
loading={isLoading && idData === e.id}
loaderPosition="center"
color="green"
leftIcon={<IconEyeCheck size={20}/>}
radius={"xl"}
onClick={() => {
setIdData(e.id);
setLoading(true);
router.push(RouterAdminInvestasi_OLD.konfirmasi + `${e.id}`);
}}
>
Detail
</Button>
</Center>
</td>
</tr>
));
return (
<>
<Stack spacing={"xs"} h={"100%"}>
@@ -158,13 +171,13 @@ function TableView({ listData }: { listData: any }) {
color={AdminColor.softBlue}
component={
<TextInput
icon={<IconSearch size={20} />}
radius={"xl"}
placeholder="Cari nama proyek"
onChange={(val) => {
onSearch(val.currentTarget.value);
}}
/>
icon={<IconSearch size={20} />}
radius={"xl"}
placeholder="Cari nama proyek"
onChange={(val) => {
onSearch(val.currentTarget.value);
}}
/>
}
/>
{/* <Group
@@ -186,8 +199,8 @@ function TableView({ listData }: { listData: any }) {
/>
</Group> */}
{_.isEmpty(data) ? (
<ComponentAdminGlobal_IsEmptyData />
{!data ? (
<CustomSkeleton height={"80vh"} width={"100%"} />
) : (
<Paper p={"md"} bg={AdminColor.softBlue} shadow="lg" h={"80vh"}>
{isShowReload && (
@@ -245,7 +258,7 @@ function TableView({ listData }: { listData: any }) {
</th>
</tr>
</thead>
<tbody>{tableBody}</tbody>
<tbody>{renderTableBody()}</tbody>
</Table>
</ScrollArea>
<Center mt={"xl"}>

View File

@@ -50,7 +50,8 @@ import { clientLogger } from "@/util/clientLogger";
import CustomSkeleton from "@/app_modules/components/CustomSkeleton";
import global_limit from "@/app/lib/limit";
import { useShallowEffect } from "@mantine/hooks";
import apiGetInvestasiCountDashboard from "../_lib/api_fetch_count_status";
import { apiGetAdminInvestasiCountDashboard } from "../_lib/api_fetch_admin_investasi";
export default function Admin_Investasi({
listInvestasi,
@@ -88,7 +89,7 @@ export default function Admin_Investasi({
async function onLoadCountPublish() {
try {
const response = await apiGetInvestasiCountDashboard({
const response = await apiGetAdminInvestasiCountDashboard({
name: "Publish",
});
@@ -103,7 +104,7 @@ export default function Admin_Investasi({
}
async function onLoadCountReview() {
try {
const response = await apiGetInvestasiCountDashboard({
const response = await apiGetAdminInvestasiCountDashboard({
name: "Review",
});
@@ -116,7 +117,7 @@ export default function Admin_Investasi({
}
async function onLoadCountReject() {
try {
const response = await apiGetInvestasiCountDashboard({
const response = await apiGetAdminInvestasiCountDashboard({
name: "Reject",
});

View File

@@ -43,6 +43,7 @@ const middlewareConfig: MiddlewareConfig = {
// "/api/admin/voting/dashboard/*",
// "/api/admin/job/dashboard/*",
// "/api/admin/forum/dashboard/*",
// Akses awal

82
templates/ff.config.json Normal file
View File

@@ -0,0 +1,82 @@
{
"name": "base",
"configs": [
{
"name": "bloc",
"commands": [
{
"name": "[FF] New Big Pack Bloc",
"templates": ["*"],
"key": "bigpack",
"files": [
"bloc",
"event",
"index",
"model",
"page",
"provider",
"repository",
"screen",
"state"
]
},
{
"name": "[FF] New Small Pack Bloc",
"templates": ["*"],
"key": "smallpack",
"files": ["bloc", "event", "index", "page", "screen", "state"]
},
{
"name": "[FF] New Bloc",
"templates": ["*"],
"files": ["bloc"]
},
{
"name": "[FF] New Event",
"templates": ["*"],
"files": ["event"]
},
{
"name": "[FF] New Model",
"templates": ["*"],
"files": ["model"]
},
{
"name": "[FF] New Page",
"templates": ["*"],
"files": ["page"]
},
{
"name": "[FF] New Provider",
"templates": ["*"],
"files": ["provider"]
},
{
"name": "[FF] New Repository",
"templates": ["*"],
"files": ["repository"]
},
{
"name": "[FF] New Screen",
"templates": ["*"],
"files": ["screen"]
},
{
"name": "[FF] New State",
"templates": ["*"],
"files": ["state"]
},
{
"name": "[FF] New Index",
"templates": ["*"],
"files": ["index"]
},
{
"name": "[FF] New Navigate(Navme)",
"templates": ["navigate"],
"files": ["navigate"]
}
]
}
]
}

View File

@@ -0,0 +1,16 @@
import 'package:ff_bloc/ff_bloc.dart';
import 'package:${appName}${relative}/index.dart';
class ${upperName}Bloc extends FFBloc<${upperName}Event, ${upperName}State> {
${upperName}Bloc({
required this.provider,
super.initialState = const ${upperName}State(),
});
/// Use this for all requests to backend - you can mock it in tests
final ${upperName}Provider provider;
@override
${upperName}State onErrorState(Object error) => state.copy(error: error, isLoading: false);
}

View File

@@ -0,0 +1,78 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:ff_bloc/ff_bloc.dart';
import 'package:${appName}${relative}/index.dart';
@immutable
abstract class ${upperName}Event implements FFBlocEvent<${upperName}State, ${upperName}Bloc> {}
/// Initial Event with load data
class Load${upperName}Event extends ${upperName}Event {
Load${upperName}Event({required this.id});
final String? id;
static const String _name = 'Load${upperName}Event';
@override
String toString() => _name;
@override
Stream<${upperName}State> applyAsync({required ${upperName}Bloc bloc}) async* {
// set loading true for show loading
yield bloc.state.copyWithoutError(isLoading: true);
// fetch data
final result = await bloc.provider.fetchAsync(id);
// set data to state
yield bloc.state.copyWithoutError(
isLoading: false,
data: ${upperName}ViewModel(items: result),
);
}
}
class Add${upperName}Event extends ${upperName}Event {
static const String _name = 'Add${upperName}Event';
@override
String toString() => _name;
@override
Stream<${upperName}State> applyAsync({required ${upperName}Bloc bloc}) async* {
yield bloc.state.copyWithoutError(isLoading: true);
final result = await bloc.provider.addMore(bloc.state.data?.items);
yield bloc.state.copyWithoutError(
isLoading: false,
data: ${upperName}ViewModel(items: result),
);
}
}
class ErrorYouAwesomeEvent extends YouAwesomeEvent {
static const String _name = 'ErrorYouAwesomeEvent';
@override
String toString() => _name;
@override
Stream<YouAwesomeState> applyAsync({required YouAwesomeBloc bloc}) async* {
throw Exception('Test error');
}
}
class Clear${upperName}Event extends ${upperName}Event {
static const String _name = 'Clear${upperName}Event';
@override
String toString() => _name;
@override
Stream<${upperName}State> applyAsync({required ${upperName}Bloc bloc}) async* {
yield bloc.state.copyWithoutError(isLoading: true);
yield bloc.state.copyWithoutData(
isLoading: false,
);
}
}

View File

View File

@@ -0,0 +1,51 @@
// ignore: depend_on_referenced_packages
import 'package:equatable/equatable.dart';
class ${upperName}Model extends Equatable {
const ${upperName}Model({
required this.name,
});
final String name;
@override
List<Object> get props => [ name];
Map<dynamic, dynamic> toMap() {
return {
'name': name,
};
}
static ${upperName}Model? fromMap(Map<dynamic, dynamic>? map) {
if (map == null) {
return null;
}
return ${upperName}Model(
name: map['name']!.toString(),
);
}
}
class ${upperName}ViewModel extends Equatable {
const ${upperName}ViewModel({
// TODO(all): add all required constructor parameters
required this.items,
});
// TODO(all): declare your fields here
final List<${upperName}Model>? items;
@override
List<Object?> get props => [items /*TODO(all): List all fields here*/];
// TODO(all): implement copyWith
${upperName}ViewModel copyWith({
List<${upperName}Model>? items,
}) {
return ${upperName}ViewModel(
items: items ?? this.items,
);
}
}

View File

@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:${appName}${relative}/index.dart';
class ${upperName}Page extends StatefulWidget {
const ${upperName}Page({
required this.bloc,
super.key
});
static const String routeName = '/${privateName}';
final ${upperName}Bloc? bloc;
@override
State<${upperName}Page> createState() => _${upperName}PageState();
}
class _${upperName}PageState extends State<${upperName}Page> {
${upperName}Bloc? _bloc;
${upperName}Bloc get bloc {
// get it by DI in real code.
_bloc ??= widget.bloc ?? ${upperName}Bloc();
return _bloc!;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text('${upperName}'),
actions: [
IconButton(
icon: const Icon(Icons.error),
onPressed: () {
bloc.add(ErrorYouAwesomeEvent());
},
),
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
bloc.add(Add${upperName}Event());
},
),
IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
bloc.add(Clear${upperName}Event());
},
),
],
),
body: ${upperName}Screen(bloc: bloc),
);
}
}

View File

@@ -0,0 +1,26 @@
import 'dart:async';
import 'package:${appName}${relative}/index.dart';
class ${upperName}Provider {
Future<List<${upperName}Model>?> fetchAsync(String? id) async {
// write logic here to send request to server
if (id == null) {
return null;
}
return [${upperName}Model(name: id)];
}
Future<List<${upperName}Model>?> addMore(List<${upperName}Model>? now) async {
// write logic here to send request to server
final result = [
...(now ?? <${upperName}Model>[]),
${upperName}Model(name: now?.length.toString() ?? '0')
];
return result;
}
}

View File

@@ -0,0 +1,126 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:${appName}${relative}/index.dart';
class ${upperName}Screen extends StatefulWidget {
const ${upperName}Screen({
required this.bloc,
super.key,
}) ;
@protected
final ${upperName}Bloc bloc;
@override
State<${upperName}Screen> createState() {
return ${upperName}ScreenState();
}
}
class ${upperName}ScreenState extends State<${upperName}Screen> {
@override
void initState() {
super.initState();
// load data on init widget if bloc has not data
if (!widget.bloc.state.hasData) {
_load();
}
}
@override
void dispose() {
// dispose bloc if you use subscriptions in bloc
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<${upperName}Bloc, ${upperName}State>(
bloc: widget.bloc,
builder: (
BuildContext context,
${upperName}State currentState,
) {
// declaration of bloc states
return currentState.when(
onLoading: ()=>const CircularProgressIndicator(),
onEmpty: (data) => _Empty(),
onData: (data) => _BodyList(data: data),
onError: (e) => Center(
child: Column(
children: [
Text(e.toString()),
TextButton(
onPressed: _load,
child: const Text('ReLoad'),
)
],
),
),
);
},
);
}
void _load() {
widget.bloc.add(Load${upperName}Event(id:'1'));
}
}
class _BodyList extends StatefulWidget {
const _BodyList({required this.data});
final ${upperName}ViewModel data;
@override
State<_BodyList> createState() => _BodyListState();
}
class _BodyListState extends State<_BodyList> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return CustomScrollView(
// primary: true,
slivers: [
const SliverToBoxAdapter(child: Divider()),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
final item = widget.data.items![index];
if (index == 0) {
return Text('Header $index, id = '+item.name);
}
return Text('Index = $index, id = '+item.name);
},
childCount: widget.data.items!.length,
))]);
}
}
class _Empty extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('Empty'),
],
);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:ff_bloc/ff_bloc.dart';
import 'package:${appName}${relative}/index.dart';
class ${upperName}State extends FFState<${upperName}State, ${upperName}ViewModel> {
const ${upperName}State({
super.version = 0,
super.isLoading = false,
super.data,
super.error,
});
@override
StateCopyFactory<${upperName}State, ${upperName}ViewModel> getCopyFactory() => ${upperName}State.new;
}

View File

@@ -0,0 +1,37 @@
import 'dart:async';
import 'dart:developer' as developer;
import 'package:bloc/bloc.dart';
import 'package:${appName}${relative}/index.dart';
class ${upperName}Bloc extends Bloc<${upperName}Event, ${upperName}State> {
// todo: check singleton for logic in project
// use GetIt for DI in projct
static final ${upperName}Bloc _${privateName}BlocSingleton = ${upperName}Bloc._internal();
factory ${upperName}Bloc() {
return _${privateName}BlocSingleton;
}
${upperName}Bloc._internal(): super(Un${upperName}State(0)){
on<${upperName}Event>((event, emit) {
return emit.forEach<${upperName}State>(
event.applyAsync(currentState: state, bloc: this),
onData: (state) => state,
onError: (error, stackTrace) {
developer.log('$error', name: '${upperName}Bloc', error: error, stackTrace: stackTrace);
return Error${upperName}State(0, error.toString());
},
);
});
}
@override
Future<void> close() async{
// dispose objects
await super.close();
}
@override
${upperName}State get initialState => Un${upperName}State(0);
}

View File

@@ -0,0 +1,42 @@
import 'dart:async';
import 'dart:developer' as developer;
import 'package:${appName}${relative}/index.dart';
import 'package:meta/meta.dart';
@immutable
abstract class ${upperName}Event {
Stream<${upperName}State> applyAsync(
{${upperName}State currentState, ${upperName}Bloc bloc});
final ${upperName}Repository _${privateName}Repository = ${upperName}Repository();
}
class Un${upperName}Event extends ${upperName}Event {
@override
Stream<${upperName}State> applyAsync({${upperName}State? currentState, ${upperName}Bloc? bloc}) async* {
yield Un${upperName}State(0);
}
}
class Load${upperName}Event extends ${upperName}Event {
final bool isError;
@override
String toString() => 'Load${upperName}Event';
Load${upperName}Event(this.isError);
@override
Stream<${upperName}State> applyAsync(
{${upperName}State? currentState, ${upperName}Bloc? bloc}) async* {
try {
yield Un${upperName}State(0);
await Future.delayed(const Duration(seconds: 1));
_${privateName}Repository.test(isError);
yield In${upperName}State(0, 'Hello world');
} catch (_, stackTrace) {
developer.log('$_', name: 'Load${upperName}Event', error: _, stackTrace: stackTrace);
yield Error${upperName}State(0, _.toString());
}
}
}

View File

View File

@@ -0,0 +1,47 @@
import 'package:equatable/equatable.dart';
/// generate by https://javiercbk.github.io/json_to_dart/
class Autogenerated${upperName} {
final List<${upperName}Model> results;
Autogenerated${upperName}({required this.results});
factory Autogenerated${upperName}.fromJson(Map<String, dynamic> json) {
var temp = <YouAwesomeModel>[];
if (json['results'] != null) {
temp = <${upperName}Model>[];
json['results'].forEach((v) {
temp.add(${upperName}Model.fromJson(v as Map<String, dynamic>));
});
}
return Autogenerated${upperName}(results: temp);
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['results'] = results.map((v) => v.toJson()).toList();
return data;
}
}
class ${upperName}Model extends Equatable {
final int id;
final String name;
${upperName}Model(this.id, this.name);
@override
List<Object> get props => [id, name];
factory ${upperName}Model.fromJson(Map<String, dynamic> json) {
return ${upperName}Model(json['id'] as int, json['name'] as String);
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
return data;
}
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:${appName}${relative}/index.dart';
class ${upperName}Page extends StatefulWidget {
static const String routeName = '/${privateName}';
@override
_${upperName}PageState createState() => _${upperName}PageState();
}
class _${upperName}PageState extends State<${upperName}Page> {
final _${privateName}Bloc = ${upperName}Bloc();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('${upperName}'),
),
body: ${upperName}Screen(${privateName}Bloc: _${privateName}Bloc),
);
}
}

View File

@@ -0,0 +1,20 @@
import 'dart:async';
class ${upperName}Provider {
Future<void> loadAsync(String token) async {
/// write from keystore/keychain
await Future.delayed(Duration(seconds: 2));
}
Future<void> saveAsync(String token) async {
/// write from keystore/keychain
await Future.delayed(Duration(seconds: 2));
}
void test(bool isError) {
if (isError == true){
throw Exception('manual error');
}
}
}

View File

@@ -0,0 +1,11 @@
import 'package:${appName}${relative}/index.dart';
class ${upperName}Repository {
final ${upperName}Provider _${privateName}Provider = ${upperName}Provider();
${upperName}Repository();
void test(bool isError) {
_${privateName}Provider.test(isError);
}
}

View File

@@ -0,0 +1,97 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:${appName}${relative}/index.dart';
class ${upperName}Screen extends StatefulWidget {
const ${upperName}Screen({
required ${upperName}Bloc ${privateName}Bloc,
Key? key,
}) : _${privateName}Bloc = ${privateName}Bloc,
super(key: key);
final ${upperName}Bloc _${privateName}Bloc;
@override
${upperName}ScreenState createState() {
return ${upperName}ScreenState();
}
}
class ${upperName}ScreenState extends State<${upperName}Screen> {
${upperName}ScreenState();
@override
void initState() {
super.initState();
_load();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<${upperName}Bloc, ${upperName}State>(
bloc: widget._${privateName}Bloc,
builder: (
BuildContext context,
${upperName}State currentState,
) {
if (currentState is Un${upperName}State) {
return Center(
child: CircularProgressIndicator(),
);
}
if (currentState is Error${upperName}State) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(currentState.errorMessage),
Padding(
padding: const EdgeInsets.only(top: 32.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
),
child: Text('reload'),
onPressed: _load,
),
),
],
));
}
if (currentState is In${upperName}State) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(currentState.hello),
const Text('Flutter files: done'),
Padding(
padding: const EdgeInsets.only(top: 32.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
child: Text('throw error'),
onPressed: () => _load(true),
),
),
],
),
);
}
return Center(
child: CircularProgressIndicator(),
);
});
}
void _load([bool isError = false]) {
widget._${privateName}Bloc.add(Load${upperName}Event(isError));
}
}

View File

@@ -0,0 +1,83 @@
import 'package:equatable/equatable.dart';
abstract class ${upperName}State extends Equatable {
${upperName}State(this.version);
/// notify change state without deep clone state
final int version;
/// Copy object for use in action
/// if need use deep clone
${upperName}State getStateCopy();
${upperName}State getNewVersion();
@override
List<Object> get props => [version];
}
/// UnInitialized
class Un${upperName}State extends ${upperName}State {
Un${upperName}State(int version) : super(version);
@override
String toString() => 'Un${upperName}State';
@override
Un${upperName}State getStateCopy() {
return Un${upperName}State(0);
}
@override
Un${upperName}State getNewVersion() {
return Un${upperName}State(version+1);
}
}
/// Initialized
class In${upperName}State extends ${upperName}State {
In${upperName}State(int version, this.hello) : super(version);
final String hello;
@override
String toString() => 'In${upperName}State $hello';
@override
In${upperName}State getStateCopy() {
return In${upperName}State(version, hello);
}
@override
In${upperName}State getNewVersion() {
return In${upperName}State(version+1, hello);
}
@override
List<Object> get props => [version, hello];
}
class Error${upperName}State extends ${upperName}State {
Error${upperName}State(int version, this.errorMessage): super(version);
final String errorMessage;
@override
String toString() => 'Error${upperName}State';
@override
Error${upperName}State getStateCopy() {
return Error${upperName}State(version, errorMessage);
}
@override
Error${upperName}State getNewVersion() {
return Error${upperName}State(version+1,
errorMessage);
}
@override
List<Object> get props => [version, errorMessage];
}

View File

@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:navme/navme.dart';
import 'package:navme/helpers.dart';
import 'index.dart';
class ${upperName}Navigate {
// base path
static String path = '${privateName}';
// config for configurate Router
static RouteConfig routeConfig = RouteConfig(
state: (Uri? uri) => RouteState(uri: path.toUri()),
// condition for using this page
isThisPage: (RouteState state) {
if (state?.firstPath == path) {
return true;
}
return false;
},
// settigs from url
settings: (RouteState state) {
return null;
},
// get Page for Router
page: ({RouteState? state}) {
return MaterialPage(
key: const ValueKey('${upperName}Page'),
child: ${upperName}Page(),
name: '${upperName}Page');
},
);
}

View File

@@ -0,0 +1,21 @@
import 'dart:async';
import 'dart:developer' as developer;
import 'package:bloc/bloc.dart';
import 'package:${appName}${relative}/index.dart';
class ${upperName}Bloc extends Bloc<${upperName}Event, ${upperName}State> {
${upperName}Bloc(${upperName}State initialState) : super(initialState){
on<${upperName}Event>((event, emit) {
return emit.forEach<${upperName}State>(
event.applyAsync(currentState: state, bloc: this),
onData: (state) => state,
onError: (error, stackTrace) {
developer.log('$error', name: '${upperName}Bloc', error: error, stackTrace: stackTrace);
return Error${upperName}State(error.toString());
},
);
});
}
}

View File

@@ -0,0 +1,34 @@
import 'dart:async';
import 'dart:developer' as developer;
import 'package:${appName}${relative}/index.dart';
import 'package:meta/meta.dart';
@immutable
abstract class ${upperName}Event {
Stream<${upperName}State> applyAsync(
{${upperName}State currentState, ${upperName}Bloc bloc});
}
class Un${upperName}Event extends ${upperName}Event {
@override
Stream<${upperName}State> applyAsync({${upperName}State? currentState, ${upperName}Bloc? bloc}) async* {
yield Un${upperName}State();
}
}
class Load${upperName}Event extends ${upperName}Event {
@override
Stream<${upperName}State> applyAsync(
{${upperName}State? currentState, ${upperName}Bloc? bloc}) async* {
try {
yield Un${upperName}State();
await Future.delayed(const Duration(seconds: 1));
yield In${upperName}State('Hello world');
} catch (_, stackTrace) {
developer.log('$_', name: 'Load${upperName}Event', error: _, stackTrace: stackTrace);
yield Error${upperName}State( _.toString());
}
}
}

View File

View File

@@ -0,0 +1,13 @@
import 'package:equatable/equatable.dart';
/// use https://marketplace.visualstudio.com/items?itemName=BendixMa.dart-data-class-generator
class ${upperName}Model extends Equatable {
final int id;
final String name;
${upperName}Model(this.id, this.name);
@override
List<Object> get props => [id, name];
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:${appName}${relative}/index.dart';
class ${upperName}Page extends StatefulWidget {
static const String routeName = '/${privateName}';
@override
_${upperName}PageState createState() => _${upperName}PageState();
}
class _${upperName}PageState extends State<${upperName}Page> {
final _${privateName}Bloc = ${upperName}Bloc(Un${upperName}State());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('${upperName}'),
),
body: ${upperName}Screen(${privateName}Bloc: _${privateName}Bloc),
);
}
}

View File

@@ -0,0 +1,20 @@
import 'dart:async';
class ${upperName}Provider {
Future<void> loadAsync(String token) async {
/// write from keystore/keychain
await Future.delayed(Duration(seconds: 2));
}
Future<void> saveAsync(String token) async {
/// write from keystore/keychain
await Future.delayed(Duration(seconds: 2));
}
void test(bool isError) {
if (isError == true){
throw Exception('manual error');
}
}
}

View File

@@ -0,0 +1,11 @@
import 'package:${appName}${relative}/index.dart';
class ${upperName}Repository {
final ${upperName}Provider _${privateName}Provider = ${upperName}Provider();
${upperName}Repository();
void test(bool isError) {
_${privateName}Provider.test(isError);
}
}

View File

@@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:${appName}${relative}/index.dart';
class ${upperName}Screen extends StatefulWidget {
const ${upperName}Screen({
required ${upperName}Bloc ${privateName}Bloc,
Key? key,
}) : _${privateName}Bloc = ${privateName}Bloc,
super(key: key);
final ${upperName}Bloc _${privateName}Bloc;
@override
${upperName}ScreenState createState() {
return ${upperName}ScreenState();
}
}
class ${upperName}ScreenState extends State<${upperName}Screen> {
${upperName}ScreenState();
@override
void initState() {
super.initState();
_load();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<${upperName}Bloc, ${upperName}State>(
bloc: widget._${privateName}Bloc,
builder: (
BuildContext context,
${upperName}State currentState,
) {
if (currentState is Un${upperName}State) {
return Center(
child: CircularProgressIndicator(),
);
}
if (currentState is Error${upperName}State) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(currentState.errorMessage ),
Padding(
padding: const EdgeInsets.only(top: 32.0),
child: RaisedButton(
color: Colors.blue,
child: Text('reload'),
onPressed: _load,
),
),
],
));
}
if (currentState is In${upperName}State) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(currentState.hello),
],
),
);
}
return Center(
child: CircularProgressIndicator(),
);
});
}
void _load() {
widget._${privateName}Bloc.add(Load${upperName}Event());
}
}

View File

@@ -0,0 +1,42 @@
import 'package:equatable/equatable.dart';
abstract class ${upperName}State extends Equatable {
${upperName}State();
@override
List<Object> get props => [];
}
/// UnInitialized
class Un${upperName}State extends ${upperName}State {
Un${upperName}State();
@override
String toString() => 'Un${upperName}State';
}
/// Initialized
class In${upperName}State extends ${upperName}State {
In${upperName}State(this.hello);
final String hello;
@override
String toString() => 'In${upperName}State $hello';
@override
List<Object> get props => [hello];
}
class Error${upperName}State extends ${upperName}State {
Error${upperName}State(this.errorMessage);
final String errorMessage;
@override
String toString() => 'Error${upperName}State';
@override
List<Object> get props => [errorMessage];
}