fixed-admin #54

Merged
bagasbanuna merged 2 commits from fixed-admin/14-feb-26 into staging 2026-02-14 16:26:43 +08:00
17 changed files with 980 additions and 1367 deletions

View File

@@ -197,7 +197,7 @@ export default function AdminEventDetail() {
/>
)}
<TextCustom align="center">{isDevLink}</TextCustom>
{/* <TextCustom align="center">{isDevLink}</TextCustom> */}
</StackCustom>
</BaseBox>
)}

View File

@@ -1,105 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BadgeCustom,
BaseBox,
Grid,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { apiAdminEventListOfParticipants } from "@/service/api-admin/api-admin-event";
import dayjs, { Dayjs } from "dayjs";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { View } from "moti";
import { useCallback, useState } from "react";
import { Admin_ScreenEventListOfParticipants } from "@/screens/Admin/Event/ScreenEventListOfParticipants";
export default function AdminEventListOfParticipants() {
const { id } = useLocalSearchParams();
const [listData, setListData] = useState<any[] | null>(null);
const [loadData, setLoadData] = useState(false);
const [startDate, setStartDate] = useState<Dayjs | undefined>();
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
setLoadData(true);
const response = await apiAdminEventListOfParticipants({
id: id as string,
});
console.log("[DATA]", JSON.stringify(response, null, 2));
if (response.success) {
setListData(response.data);
setStartDate(dayjs(response.data.Event.tanggal));
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadData(false);
}
};
return (
<>
<ViewWrapper
headerComponent={<AdminBackButtonAntTitle title="Daftar Peserta" />}
>
{loadData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center" color="gray">
Belum ada peserta
</TextCustom>
) : (
listData?.map((item: any, index: number) => (
<BaseBox key={index}>
<Grid>
<Grid.Col span={6}>
<StackCustom gap={"sm"}>
<TextCustom bold truncate>
{item?.User?.username}
</TextCustom>
<TextCustom>+{item?.User?.nomor}</TextCustom>
</StackCustom>
</Grid.Col>
<Grid.Col span={6} style={{ justifyContent: "center" }}>
{startDate &&
startDate.subtract(1, "hour").diff(dayjs()) < 0 ? (
<BadgeCustom
style={{ alignSelf: "flex-end" }}
color={item?.isPresent ? "green" : "red"}
>
{item?.isPresent ? "Hadir" : "Tidak Hadir"}
</BadgeCustom>
) : (
<View
style={{
justifyContent: "flex-end",
}}
>
<BadgeCustom
style={{ alignSelf: "flex-end" }}
color="gray"
>
-
</BadgeCustom>
</View>
)}
</Grid.Col>
</Grid>
</BaseBox>
))
)}
</ViewWrapper>
</>
);
return <Admin_ScreenEventListOfParticipants />;
}

View File

@@ -1,135 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
ClickableCustom,
LoaderCustom,
SearchInput,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminEvent } from "@/service/api-admin/api-admin-event";
import { dateTimeView } from "@/utils/dateTimeView";
import { Octicons } from "@expo/vector-icons";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
import { Admin_ScreenEventStatus } from "@/screens/Admin/Event/ScreenEventStatus";
export default function AdminEventStatus() {
const { status } = useLocalSearchParams();
console.log("[STATUS EVENT]", status);
const [listData, setListData] = useState<any[] | null>(null);
const [loadData, setLoadData] = useState(false);
const [search, setSearch] = useState<string>("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [status, search])
);
const onLoadData = async () => {
try {
setLoadData(true);
const response = await apiAdminEvent({
category: status as "publish" | "review" | "reject" | "history" as any,
search,
});
console.log(
`[RES LIST BY STATUS: ${status}]`,
JSON.stringify(response, null, 2)
);
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadData(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
value={search}
onChangeText={(value) => setSearch(value)}
/>
);
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Event" />}>
<AdminComp_BoxTitle
title={`${_.startCase(status as string)}`}
rightComponent={rightComponent}
/>
<StackCustom gap={"sm"}>
<AdminTitleTable
title1="Username"
title2="Tanggal"
title3="Judul Event"
/>
<Divider />
{loadData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center" size="small" color="gray">
Belum ada data
</TextCustom>
) : (
listData?.map((item, index) => (
<ClickableCustom
key={index}
onPress={() => {
router.push(`/admin/event/${item.id}/${status}`);
}}
>
<AdminTableValue
key={index}
value1={
<TextCustom truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
// <ActionIcon
// icon={
// <Octicons
// name="eye"
// size={ICON_SIZE_BUTTON}
// color="black"
// />
// }
// onPress={() => {
// router.push(`/admin/event/${item.id}/${status}`);
// }}
// />
}
value2={
<TextCustom truncate={1}>
{dateTimeView({ date: item?.tanggal })}
</TextCustom>
}
value3={
<TextCustom truncate={2}>{item?.title || "-"}</TextCustom>
}
/>
<Divider/>
</ClickableCustom>
))
)}
</StackCustom>
</ViewWrapper>
</>
);
return <Admin_ScreenEventStatus />;
}

View File

@@ -5,6 +5,7 @@ import {
BaseBox,
DummyLandscapeImage,
Grid,
NewWrapper,
Spacing,
StackCustom,
TextCustom,
@@ -120,7 +121,7 @@ export default function AdminJobDetailStatus() {
return (
<>
<ViewWrapper
<NewWrapper
headerComponent={<AdminBackButtonAntTitle title={`Detail Data`} />}
>
<BaseBox>
@@ -184,7 +185,7 @@ export default function AdminJobDetailStatus() {
/>
)}
<Spacing />
</ViewWrapper>
</NewWrapper>
</>
);
}

View File

@@ -2,6 +2,7 @@
import {
AlertDefaultSystem,
BoxButtonOnFooter,
NewWrapper,
TextAreaCustom,
ViewWrapper,
} from "@/components";
@@ -100,7 +101,7 @@ export default function AdminJobRejectInput() {
return (
<>
<ViewWrapper
<NewWrapper
footerComponent={buttonSubmit}
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
>
@@ -112,7 +113,7 @@ export default function AdminJobRejectInput() {
showCount
maxLength={1000}
/>
</ViewWrapper>
</NewWrapper>
</>
);
}

View File

@@ -1,117 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
LoaderCustom,
SearchInput,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminJob } from "@/service/api-admin/api-admin-job";
import { Octicons } from "@expo/vector-icons";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
import { Admin_ScreenJobStatus } from "@/screens/Admin/Job/ScreenJobStatus";
export default function AdminJobStatus() {
const { status } = useLocalSearchParams();
const [list, setList] = useState<any | null>(null);
const [loadList, setLoadList] = useState(false);
const [search, setSearch] = useState("");
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [status, search])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminJob({
category: status as "publish" | "review" | "reject",
search,
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = (
<SearchInput
placeholder="Cari"
onChangeText={setSearch}
value={search}
/>
);
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Job Vacancy" />}>
<AdminComp_BoxTitle
title={`${_.startCase(status as string)}`}
rightComponent={rightComponent}
/>
<StackCustom>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Pekerjaan"
/>
{/* <Spacing /> */}
<Divider />
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom align="center" color="gray">
Tidak ada data
</TextCustom>
) : (
list?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/job/${item.id}/${status}`);
}}
/>
}
value2={
<TextCustom align="center" truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);
return <Admin_ScreenJobStatus />;
}

1512
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -22,8 +22,8 @@ Jika tidak ada props page maka tambahkan props page dan default page: "1"
Gunakan bahasa indonesia pada cli agar saya mudah membacanya.
<!-- Additional Prompt -->
File refrensi: screens/Donation/ScreenListOfNews.tsx
Anda bisa menggunakan refrensi dari "File refrensi" jika butuh pemahaman dengan tipe fitur yang sama
File refrensi: screens/Admin/Event/ScreenEventStatus.tsx
Anda bisa menggunakan refrensi dari "File refrensi" jika butuh pemahaman dengan tipe fitur yang hampir sama
<!-- ===================== End Penerapan Pagination ` ===================== -->
@@ -61,10 +61,10 @@ Gunakan bahasa indonesia pada cli agar saya mudah membacanya.eclar
<!-- START Prompt Admin Refactoring -->
<!-- Pindah kode ke Screen Component -->
File source: app/(application)/admin/app-information/business-field/[id]/index.tsx
Folder tujuan: screens/Admin/App-Information
Nama file utama: ScreenBusinessFieldDetail.tsx
Nama function utama: Admin_ScreenBusinessFieldDetail
File source: app/(application)/admin/event/[id]/list-of-participants.tsx
Folder tujuan: screens/Admin/Event
Nama file utama: ScreenEventListOfParticipants.tsx
Nama function utama: Admin_ScreenEventListOfParticipants
File komponen wrapper: components/_ShareComponent/NewWrapper.tsx
Buat file baru pada "Folder tujuan" dengan nama "Nama file utama" dan ubah nama function menjadi "Nama function utama" kemudian clean code, import dan panggil function tersebut pada file "File source"
@@ -72,15 +72,14 @@ Analisa juga file "Nama file utama" , jika belum menggunakan NewWrapper pada fil
<!-- Penerapan Pagination -->
Function fecth: apiAdminMasterBank
File function fetch: service/api-admin/api-master-admin.ts
Function fecth: apiAdminEventListOfParticipants
File function fetch: service/api-admin/api-admin-event.ts
Terapkan pagination pada file "Nama file utama"
Komponen pagination yang digunaka berada pada file hooks/use-pagination.tsx dan helpers/paginationHelpers.tsx
Perbaiki fetch "Function fecth" , pada file "File function fetch"
Jika tidak ada props page maka tambahkan props page dan default page: "1"
Jika tidak ada props page maka tambahkan props page dan default page: "1" ( string )
Kemudian rapikan code nya pisah komponen seperti render item dan lainnya agar lebih rapi dan di dalam return panggil komponen tersebut
Gunakan bahasa indonesia pada cli agar saya mudah membacanya.
<!-- END Prompt Admin Refactoring -->

View File

@@ -33,7 +33,7 @@
"expo-dev-client": "~6.0.12",
"expo-device": "^8.0.9",
"expo-document-picker": "~14.0.7",
"expo-file-system": "^19.0.15",
"expo-file-system": "^19.0.21",
"expo-font": "~14.0.8",
"expo-haptics": "~15.0.7",
"expo-image": "~3.0.8",

View File

@@ -0,0 +1,55 @@
import {
BadgeCustom,
BaseBox,
Grid,
StackCustom,
TextCustom
} from "@/components";
import dayjs from "dayjs";
import { View } from "moti";
interface Admin_BoxEventParticipantProps {
item: any;
startDate?: dayjs.Dayjs;
}
export function Admin_BoxEventParticipant({
item,
startDate,
}: Admin_BoxEventParticipantProps) {
return (
<BaseBox>
<Grid>
<Grid.Col span={6}>
<StackCustom gap={"sm"}>
<TextCustom bold truncate>
{item?.User?.username}
</TextCustom>
<TextCustom>+{item?.User?.nomor}</TextCustom>
</StackCustom>
</Grid.Col>
<Grid.Col span={6} style={{ justifyContent: "center" }}>
{startDate && startDate.subtract(1, "hour").diff(dayjs()) < 0 ? (
<BadgeCustom
style={{ alignSelf: "flex-end" }}
color={item?.isPresent ? "green" : "red"}
>
{item?.isPresent ? "Hadir" : "Tidak Hadir"}
</BadgeCustom>
) : (
<View
style={{
justifyContent: "flex-end",
}}
>
<BadgeCustom style={{ alignSelf: "flex-end" }} color="gray">
-
</BadgeCustom>
</View>
)}
</Grid.Col>
</Grid>
</BaseBox>
);
}

View File

@@ -0,0 +1,48 @@
import { StackCustom, TextCustom } from "@/components";
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import { dateTimeView } from "@/utils/dateTimeView";
import { router } from "expo-router";
import { View } from "react-native";
import { Divider } from "react-native-paper";
interface Admin_BoxEventStatusProps {
item: any;
status: string;
}
export function Admin_BoxEventStatus({ item, status }: Admin_BoxEventStatusProps) {
return (
<AdminBasicBox
style={{ marginHorizontal: 10, marginVertical: 5 }}
onPress={() => {
router.push(`/admin/event/${item.id}/${status}`);
}}
>
<StackCustom gap={0}>
<View style={{ paddingBlock: 8 }}>
<TextCustom size={"large"} bold truncate={2}>
{item?.title || "-"}
</TextCustom>
</View>
<Divider />
<GridSpan_4_8
label={<TextCustom>Mulai</TextCustom>}
value={
<TextCustom>
{dateTimeView({ date: item?.tanggal }) || "-"}
</TextCustom>
}
/>
<GridSpan_4_8
label={<TextCustom>Berakhir</TextCustom>}
value={
<TextCustom>
{dateTimeView({ date: item?.tanggalSelesai }) || "-"}
</TextCustom>
}
/>
</StackCustom>
</AdminBasicBox>
);
}

View File

@@ -0,0 +1,83 @@
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
import { usePagination } from "@/hooks/use-pagination";
import { apiAdminEventListOfParticipants } from "@/service/api-admin/api-admin-event";
import dayjs from "dayjs";
import { useLocalSearchParams } from "expo-router";
import { useCallback } from "react";
import { RefreshControl } from "react-native";
import { Admin_BoxEventParticipant } from "./BoxEventParticipant";
export function Admin_ScreenEventListOfParticipants() {
const { id } = useLocalSearchParams();
// Gunakan hook pagination
const pagination = usePagination({
fetchFunction: async (page, searchQuery) => {
const response = await apiAdminEventListOfParticipants({
id: id as string,
page: String(page),
});
if (response.success) {
return { data: response.data };
} else {
return { data: [] };
}
},
pageSize: PAGINATION_DEFAULT_TAKE,
dependencies: [id],
onError: (error) => {
console.error("Error loading participants:", error);
},
});
// Render item untuk daftar peserta
const renderItem = useCallback(
({ item, index }: { item: any; index: number }) => (
<Admin_BoxEventParticipant
key={index}
item={item}
startDate={dayjs(item?.Event?.tanggal)}
/>
),
[],
);
// Buat komponen-komponen pagination
const { ListEmptyComponent, ListFooterComponent } =
createPaginationComponents({
loading: pagination.loading,
refreshing: pagination.refreshing,
listData: pagination.listData,
searchQuery: "",
emptyMessage: "Belum ada peserta",
emptySearchMessage: "Tidak ada hasil pencarian",
isInitialLoad: pagination.isInitialLoad,
skeletonCount: PAGINATION_DEFAULT_TAKE,
skeletonHeight: 60,
});
return (
<NewWrapper
listData={pagination.listData}
renderItem={renderItem}
keyExtractor={(item: any) => item.id.toString()}
headerComponent={<AdminBackButtonAntTitle title="Daftar Peserta" />}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
onEndReached={pagination.loadMore}
refreshControl={
<RefreshControl
refreshing={pagination.refreshing}
onRefresh={pagination.onRefresh}
tintColor={MainColor.yellow}
colors={[MainColor.yellow]}
/>
}
/>
);
}

View File

@@ -0,0 +1,108 @@
import { SearchInput } from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
import { usePagination } from "@/hooks/use-pagination";
import { apiAdminEvent } from "@/service/api-admin/api-admin-event";
import { useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useMemo, useState } from "react";
import { RefreshControl } from "react-native";
import { Admin_BoxEventStatus } from "./BoxEventStatus";
export function Admin_ScreenEventStatus() {
const { status } = useLocalSearchParams();
const [search, setSearch] = useState<string>("");
// Gunakan hook pagination
const pagination = usePagination({
fetchFunction: async (page, searchQuery) => {
const response = await apiAdminEvent({
category: status as
| "publish"
| "review"
| "history"
| "dashboard"
| "type-of-event",
search: searchQuery,
page: String(page),
});
if (response.success) {
return { data: response.data };
} else {
return { data: [] };
}
},
pageSize: PAGINATION_DEFAULT_TAKE,
searchQuery: search,
dependencies: [status],
});
// Komponen kanan untuk header
const rightComponent = useMemo(
() => (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
value={search}
onChangeText={(value) => setSearch(value)}
/>
),
[search],
);
// Render item untuk daftar event
const renderItem = useCallback(
({ item, index }: { item: any; index: number }) => (
<Admin_BoxEventStatus key={index} item={item} status={status as string} />
),
[status],
);
const headerComponent = useMemo(
() => (
<AdminComp_BoxTitle
title={`Event ${_.startCase(status as string)}`}
rightComponent={rightComponent}
/>
),
[status, rightComponent],
);
// Buat komponen-komponen pagination
const { ListEmptyComponent, ListFooterComponent } =
createPaginationComponents({
loading: pagination.loading,
refreshing: pagination.refreshing,
listData: pagination.listData,
searchQuery: search,
emptyMessage: "Belum ada data",
emptySearchMessage: "Tidak ada hasil pencarian",
isInitialLoad: pagination.isInitialLoad,
skeletonCount: PAGINATION_DEFAULT_TAKE,
skeletonHeight: 100,
});
return (
<NewWrapper
listData={pagination.listData}
renderItem={renderItem}
keyExtractor={(item: any) => item.id.toString()}
headerComponent={headerComponent}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
onEndReached={pagination.loadMore}
refreshControl={
<RefreshControl
refreshing={pagination.refreshing}
onRefresh={pagination.onRefresh}
tintColor={MainColor.yellow}
colors={[MainColor.yellow]}
/>
}
/>
);
}

View File

@@ -0,0 +1,29 @@
import { Spacing, StackCustom, TextCustom } from "@/components";
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
import { router } from "expo-router";
import { View } from "react-native";
import { Divider } from "react-native-paper";
interface BoxStatusJobProps {
item: any;
status: string;
}
export function BoxStatusJob({ item, status }: BoxStatusJobProps) {
return (
<AdminBasicBox
style={{ marginHorizontal: 10, marginVertical: 5 }}
onPress={() => {
router.push(`/admin/job/${item.id}/${status}`);
}}
>
<StackCustom>
<View style={{paddingBlock: 8}}>
<TextCustom size={"large"} align="center" bold truncate={2}>
{item?.title || "-"}
</TextCustom>
</View>
</StackCustom>
</AdminBasicBox>
);
}

View File

@@ -0,0 +1,103 @@
import { SearchInput } from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
import { usePagination } from "@/hooks/use-pagination";
import { apiAdminJob } from "@/service/api-admin/api-admin-job";
import { router, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useMemo, useState } from "react";
import { RefreshControl } from "react-native";
import { Divider } from "react-native-paper";
import { BoxStatusJob } from "./BoxStatusJob";
export function Admin_ScreenJobStatus() {
const { status } = useLocalSearchParams();
const [search, setSearch] = useState("");
// Gunakan hook pagination
const pagination = usePagination({
fetchFunction: async (page, searchQuery) => {
const response = await apiAdminJob({
category: status as "publish" | "review" | "reject",
search: searchQuery,
page: String(page),
});
if (response.success) {
return { data: response.data };
} else {
return { data: [] };
}
},
pageSize: PAGINATION_DEFAULT_TAKE,
searchQuery: search,
dependencies: [status],
});
// Komponen kanan untuk header
const rightComponent = useMemo(
() => (
<SearchInput
placeholder="Cari perkerjaan"
onChangeText={setSearch}
value={search}
/>
),
[search],
);
// Render item untuk daftar pekerjaan
const renderItem = useCallback(
({ item, index }: { item: any; index: number }) => (
<BoxStatusJob key={index} item={item} status={status as string} />
),
[status],
);
const headerComponent = useMemo(
() => (
<AdminComp_BoxTitle
title={`Job ${_.startCase(status as string)}`}
rightComponent={rightComponent}
/>
),
[status, rightComponent],
);
// Buat komponen-komponen pagination
const { ListEmptyComponent, ListFooterComponent } =
createPaginationComponents({
loading: pagination.loading,
refreshing: pagination.refreshing,
listData: pagination.listData,
searchQuery: search,
emptyMessage: "Tidak ada data",
emptySearchMessage: "Tidak ada hasil pencarian",
isInitialLoad: pagination.isInitialLoad,
skeletonCount: PAGINATION_DEFAULT_TAKE,
skeletonHeight: 100,
});
return (
<NewWrapper
listData={pagination.listData}
renderItem={renderItem}
keyExtractor={(item: any) => item.id.toString()}
headerComponent={headerComponent}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
onEndReached={pagination.loadMore}
refreshControl={
<RefreshControl
refreshing={pagination.refreshing}
onRefresh={pagination.onRefresh}
tintColor={MainColor.yellow}
colors={[MainColor.yellow]}
/>
}
/>
);
}

View File

@@ -3,13 +3,15 @@ import { apiConfig } from "../api-config";
export async function apiAdminEvent({
category,
search,
page = "1",
}: {
category: "dashboard" | "history" | "publish" | "review" | "type-of-event";
search?: string;
page?: string;
}) {
try {
const response = await apiConfig.get(
`/mobile/admin/event?category=${category}&search=${search}`
`/mobile/admin/event?category=${category}&search=${search}&page=${page}`
);
return response.data;
} catch (error) {
@@ -48,10 +50,18 @@ export async function apiAdminEventUpdateStatus({
}
}
export async function apiAdminEventListOfParticipants({ id }: { id: string }) {
export async function apiAdminEventListOfParticipants({
id,
page = "1",
search = ""
}: {
id: string;
page?: string;
search?: string;
}) {
try {
const response = await apiConfig.get(
`/mobile/admin/event/${id}/participants`
`/mobile/admin/event/${id}/participants?page=${page}&search=${search}`
);
return response.data;
} catch (error) {

View File

@@ -3,13 +3,15 @@ import { apiConfig } from "../api-config";
export async function apiAdminJob({
category,
search,
page = "1",
}: {
category: "dashboard" | "publish" | "review" | "reject";
search?: string;
page?: string;
}) {
try {
const response = await apiConfig.get(
`/mobile/admin/job?category=${category}&search=${search}`
`/mobile/admin/job?category=${category}&search=${search}&page=${page}`
);
return response.data;
} catch (error) {