Ringkasan Perubahan
1. Penambahan Pagination pada Fitur Admin Job
- Menerapkan sistem pagination menggunakan hook usePagination dari hooks/use-pagination.tsx
- Mengintegrasikan komponen-komponen pagination dari helpers/paginationHelpers.tsx
- Menambahkan dukungan infinite scroll dan pull-to-refresh
- Menambahkan loading state, skeleton loader, dan empty state
2. Pembaruan Fungsi API
- Memperbarui fungsi apiAdminJob di service/api-admin/api-admin-job.ts untuk mendukung parameter
pagination
- Menambahkan parameter page dengan nilai default 1
3. Modularisasi Kode
- Memindahkan komponen AdminJobStatus dari app/(application)/admin/job/[status]/status.tsx ke
screens/Admin/Job/ScreenJobStatus.tsx
- Mengganti ViewWrapper dengan NewWrapper untuk tampilan yang lebih fleksibel
- Membuat komponen baru BoxStatusJob.tsx untuk memisahkan logika tampilan item pekerjaan
- Menggunakan komponen BoxStatusJob di dalam ScreenJobStatus untuk menampilkan daftar pekerjaan
4. Perbaikan Struktur dan Organisasi Kode
- Mengorganisir ulang struktur folder untuk komponen admin job
- Memisahkan tanggung jawab antara komponen layar dan komponen item
- Mengoptimalkan performa dengan menggunakan useCallback dan useMemo
File-file yang Diubah
1. screens/Admin/Job/ScreenJobStatus.tsx - Implementasi utama dengan pagination
2. screens/Admin/Job/BoxStatusJob.tsx - Komponen baru untuk menampilkan item pekerjaan
3. service/api-admin/api-admin-job.ts - Penambahan parameter pagination
4. app/(application)/admin/job/[status]/status.tsx - Diperbarui untuk menggunakan komponen baru
### NO Issue
This commit is contained in:
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 />;
|
||||
}
|
||||
|
||||
@@ -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/job/[status]/status.tsx
|
||||
Folder tujuan: screens/Admin/Job
|
||||
Nama file utama: ScreenJobStatus.tsx
|
||||
Nama function utama: Admin_ScreenJobStatus
|
||||
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: apiAdminJob
|
||||
File function fetch: service/api-admin/api-admin-job.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 -->
|
||||
|
||||
29
screens/Admin/Job/BoxStatusJob.tsx
Normal file
29
screens/Admin/Job/BoxStatusJob.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
103
screens/Admin/Job/ScreenJobStatus.tsx
Normal file
103
screens/Admin/Job/ScreenJobStatus.tsx
Normal 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]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user