Fix job notifikasi #35
@@ -519,7 +519,8 @@ export default function UserLayout() {
|
|||||||
name="job/(tabs)"
|
name="job/(tabs)"
|
||||||
options={{
|
options={{
|
||||||
title: "Job Vacancy",
|
title: "Job Vacancy",
|
||||||
headerLeft: () => <BackButton path="/home" />,
|
// headerLeft: () => <BackButton path="/home" />,
|
||||||
|
// Note: headerLeft di pindahkan ke Tabs Layout
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|||||||
@@ -1,34 +1,72 @@
|
|||||||
|
import { BackButton } from "@/components";
|
||||||
import { IconHome, IconStatus } from "@/components/_Icon";
|
import { IconHome, IconStatus } from "@/components/_Icon";
|
||||||
import { TabsStyles } from "@/styles/tabs-styles";
|
import { TabsStyles } from "@/styles/tabs-styles";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { Tabs } from "expo-router";
|
import {
|
||||||
|
Stack,
|
||||||
|
Tabs,
|
||||||
|
useLocalSearchParams,
|
||||||
|
router,
|
||||||
|
useNavigation,
|
||||||
|
} from "expo-router";
|
||||||
|
import { useLayoutEffect } from "react";
|
||||||
|
|
||||||
export default function JobTabsLayout() {
|
export default function JobTabsLayout() {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
const { from, category } = useLocalSearchParams<{
|
||||||
|
from?: string;
|
||||||
|
category?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// Atur header secara dinamis
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
|
headerLeft: () => (
|
||||||
|
<BackButton
|
||||||
|
onPress={() => {
|
||||||
|
if (from === "notifications") {
|
||||||
|
router.replace(`/notifications?category=${category}`);
|
||||||
|
} else {
|
||||||
|
if (from) {
|
||||||
|
router.replace(`/${from}` as any);
|
||||||
|
} else {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}, [from, router, navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs screenOptions={TabsStyles}>
|
<>
|
||||||
<Tabs.Screen
|
<Tabs screenOptions={TabsStyles}>
|
||||||
name="index"
|
<Tabs.Screen
|
||||||
options={{
|
name="index"
|
||||||
title: "Beranda",
|
options={{
|
||||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
title: "Beranda",
|
||||||
}}
|
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||||
/>
|
}}
|
||||||
<Tabs.Screen
|
/>
|
||||||
name="status"
|
<Tabs.Screen
|
||||||
options={{
|
name="status"
|
||||||
title: "Status",
|
options={{
|
||||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
title: "Status",
|
||||||
}}
|
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||||
/>
|
}}
|
||||||
<Tabs.Screen
|
/>
|
||||||
name="archive"
|
<Tabs.Screen
|
||||||
options={{
|
name="archive"
|
||||||
title: "Arsip",
|
options={{
|
||||||
tabBarIcon: ({ color }) => (
|
title: "Arsip",
|
||||||
<Ionicons size={20} name="archive" color={color} />
|
tabBarIcon: ({ color }) => (
|
||||||
),
|
<Ionicons size={20} name="archive" color={color} />
|
||||||
}}
|
),
|
||||||
/>
|
}}
|
||||||
</Tabs>
|
/>
|
||||||
|
</Tabs>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,17 @@ import {
|
|||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
||||||
import { apiJobGetByStatus } from "@/service/api-client/api-job";
|
import { apiJobGetByStatus } from "@/service/api-client/api-job";
|
||||||
import { useFocusEffect } from "expo-router";
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function JobStatus() {
|
export default function JobStatus() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const { status } = useLocalSearchParams<{ status?: string }>();
|
||||||
|
console.log("STATUS", status);
|
||||||
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||||
"publish"
|
status || "publish"
|
||||||
);
|
);
|
||||||
const [listData, setListData] = useState<any[]>([]);
|
const [listData, setListData] = useState<any[]>([]);
|
||||||
const [isLoadList, setIsLoadList] = useState(false);
|
const [isLoadList, setIsLoadList] = useState(false);
|
||||||
@@ -60,25 +63,29 @@ export default function JobStatus() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
<>
|
||||||
{isLoadList ? (
|
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
||||||
<LoaderCustom />
|
{isLoadList ? (
|
||||||
) : _.isEmpty(listData) ? (
|
<LoaderCustom />
|
||||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
) : _.isEmpty(listData) ? (
|
||||||
) : (
|
<TextCustom align="center">
|
||||||
listData.map((e, i) => (
|
Tidak ada data {activeCategory}
|
||||||
<BaseBox
|
</TextCustom>
|
||||||
key={i}
|
) : (
|
||||||
paddingTop={20}
|
listData.map((e, i) => (
|
||||||
paddingBottom={20}
|
<BaseBox
|
||||||
href={`/job/${e?.id}/${activeCategory}/detail`}
|
key={i}
|
||||||
>
|
paddingTop={20}
|
||||||
<TextCustom align="center" bold truncate size="large">
|
paddingBottom={20}
|
||||||
{e?.title}
|
href={`/job/${e?.id}/${activeCategory}/detail`}
|
||||||
</TextCustom>
|
>
|
||||||
</BaseBox>
|
<TextCustom align="center" bold truncate size="large">
|
||||||
))
|
{e?.title}
|
||||||
)}
|
</TextCustom>
|
||||||
</ViewWrapper>
|
</BaseBox>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default function JobDetailStatus() {
|
|||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
{data &&
|
{data &&
|
||||||
data?.catatan &&
|
data?.catatan &&
|
||||||
(status === "draft" || status === "rejected") && (
|
(status === "draft" || status === "reject") && (
|
||||||
<ReportBox text={data?.catatan} />
|
<ReportBox text={data?.catatan} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { useState } from "react";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function JobCreate() {
|
export default function JobCreate() {
|
||||||
const nextUrl = "/(application)/(user)/job/(tabs)/status";
|
const nextUrl = "/(application)/(user)/job/(tabs)/status?status=review";
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [image, setImage] = useState<string | null>(null);
|
const [image, setImage] = useState<string | null>(null);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { useNotificationStore } from "@/hooks/use-notification-store";
|
|||||||
import { apiGetNotificationsById } from "@/service/api-notifications";
|
import { apiGetNotificationsById } from "@/service/api-notifications";
|
||||||
import { listOfcategoriesAppNotification } from "@/types/type-notification-category";
|
import { listOfcategoriesAppNotification } from "@/types/type-notification-category";
|
||||||
import { formatChatTime } from "@/utils/formatChatTime";
|
import { formatChatTime } from "@/utils/formatChatTime";
|
||||||
import { router, useFocusEffect } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { RefreshControl, View } from "react-native";
|
import { RefreshControl, View } from "react-native";
|
||||||
@@ -25,6 +25,19 @@ const selectedCategory = (value: string) => {
|
|||||||
return category?.label;
|
return category?.label;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fixPath = ({
|
||||||
|
deepLink,
|
||||||
|
categoryApp,
|
||||||
|
}: {
|
||||||
|
deepLink: string;
|
||||||
|
categoryApp: string;
|
||||||
|
}) => {
|
||||||
|
const fixPath =
|
||||||
|
deepLink + "&from=notifications&category=" + _.lowerCase(categoryApp);
|
||||||
|
|
||||||
|
return fixPath;
|
||||||
|
};
|
||||||
|
|
||||||
const BoxNotification = ({
|
const BoxNotification = ({
|
||||||
data,
|
data,
|
||||||
activeCategory,
|
activeCategory,
|
||||||
@@ -32,18 +45,28 @@ const BoxNotification = ({
|
|||||||
data: any;
|
data: any;
|
||||||
activeCategory: string | null;
|
activeCategory: string | null;
|
||||||
}) => {
|
}) => {
|
||||||
|
// console.log("DATA NOTIFICATION", JSON.stringify(data, null, 2));
|
||||||
const { markAsRead } = useNotificationStore();
|
const { markAsRead } = useNotificationStore();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BaseBox
|
<BaseBox
|
||||||
backgroundColor={data.isRead ? AccentColor.darkblue : AccentColor.blue}
|
backgroundColor={data.isRead ? AccentColor.darkblue : AccentColor.blue}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
console.log(
|
// console.log(
|
||||||
"Notification >",
|
// "Notification >",
|
||||||
selectedCategory(activeCategory as string)
|
// selectedCategory(activeCategory as string)
|
||||||
);
|
// );
|
||||||
router.push(data.deepLink);
|
const newPath = fixPath({
|
||||||
markAsRead(data.id);
|
deepLink: data.deepLink,
|
||||||
|
categoryApp: data.kategoriApp,
|
||||||
|
});
|
||||||
|
|
||||||
|
router.replace(newPath as any);
|
||||||
|
selectedCategory(activeCategory as string);
|
||||||
|
|
||||||
|
if (!data.isRead) {
|
||||||
|
markAsRead(data.id);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
@@ -64,7 +87,10 @@ const BoxNotification = ({
|
|||||||
|
|
||||||
export default function Notifications() {
|
export default function Notifications() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>("event");
|
const { category } = useLocalSearchParams<{ category?: string }>();
|
||||||
|
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||||
|
category || "event"
|
||||||
|
);
|
||||||
const [listData, setListData] = useState<any[]>([]);
|
const [listData, setListData] = useState<any[]>([]);
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ import Toast from "react-native-toast-message";
|
|||||||
|
|
||||||
export default function AdminJobRejectInput() {
|
export default function AdminJobRejectInput() {
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
const [data, setData] = useState<any | null>(null);
|
const [data, setData] = useState({
|
||||||
|
catatan: "",
|
||||||
|
senderId: ""
|
||||||
|
});
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
@@ -48,7 +51,7 @@ export default function AdminJobRejectInput() {
|
|||||||
const response = await funUpdateStatusJob({
|
const response = await funUpdateStatusJob({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
changeStatus,
|
changeStatus,
|
||||||
data: data,
|
data: data ,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
@@ -102,8 +105,8 @@ export default function AdminJobRejectInput() {
|
|||||||
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
|
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
|
||||||
>
|
>
|
||||||
<TextAreaCustom
|
<TextAreaCustom
|
||||||
value={data}
|
value={data?.catatan}
|
||||||
onChangeText={setData}
|
onChangeText={(text) => setData({ ...data, catatan: text })}
|
||||||
placeholder="Masukan alasan"
|
placeholder="Masukan alasan"
|
||||||
required
|
required
|
||||||
showCount
|
showCount
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import {
|
|||||||
TextCustom,
|
TextCustom,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconPlus } from "@/components/_Icon";
|
import { IconPlus } from "@/components/_Icon";
|
||||||
|
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||||
|
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
||||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { useNotificationStore } from "@/hooks/use-notification-store";
|
import { useNotificationStore } from "@/hooks/use-notification-store";
|
||||||
@@ -14,6 +16,7 @@ import { apiGetNotificationsById } from "@/service/api-notifications";
|
|||||||
import { listOfcategoriesAppNotification } from "@/types/type-notification-category";
|
import { listOfcategoriesAppNotification } from "@/types/type-notification-category";
|
||||||
import { formatChatTime } from "@/utils/formatChatTime";
|
import { formatChatTime } from "@/utils/formatChatTime";
|
||||||
import { router, Stack, useFocusEffect } from "expo-router";
|
import { router, Stack, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { RefreshControl, View } from "react-native";
|
import { RefreshControl, View } from "react-native";
|
||||||
|
|
||||||
@@ -66,6 +69,7 @@ export default function AdminNotification() {
|
|||||||
const [activeCategory, setActiveCategory] = useState<string | null>("event");
|
const [activeCategory, setActiveCategory] = useState<string | null>("event");
|
||||||
const [listData, setListData] = useState<any[]>([]);
|
const [listData, setListData] = useState<any[]>([]);
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
const handlePress = (item: any) => {
|
||||||
setActiveCategory(item.value);
|
setActiveCategory(item.value);
|
||||||
@@ -80,6 +84,7 @@ export default function AdminNotification() {
|
|||||||
|
|
||||||
const fecthData = async () => {
|
const fecthData = async () => {
|
||||||
try {
|
try {
|
||||||
|
setLoading(true);
|
||||||
const response = await apiGetNotificationsById({
|
const response = await apiGetNotificationsById({
|
||||||
id: user?.id as any,
|
id: user?.id as any,
|
||||||
category: activeCategory as any,
|
category: activeCategory as any,
|
||||||
@@ -92,6 +97,8 @@ export default function AdminNotification() {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Error Notification", error);
|
console.log("Error Notification", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,11 +139,20 @@ export default function AdminNotification() {
|
|||||||
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
|
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{listData.map((e, i) => (
|
{loading ? (
|
||||||
<View key={i}>
|
<ListSkeletonComponent />
|
||||||
<BoxNotification data={e} activeCategory={activeCategory as any} />
|
) : _.isEmpty(listData) ? (
|
||||||
</View>
|
<NoDataText text="Belum ada notifikasi" />
|
||||||
))}
|
) : (
|
||||||
|
listData.map((e, i) => (
|
||||||
|
<View key={i}>
|
||||||
|
<BoxNotification
|
||||||
|
data={e}
|
||||||
|
activeCategory={activeCategory as any}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</NewWrapper>
|
</NewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ const LeftButtonCustom = ({
|
|||||||
path,
|
path,
|
||||||
icon = "arrow-back",
|
icon = "arrow-back",
|
||||||
iconCustom,
|
iconCustom,
|
||||||
|
onPress,
|
||||||
}: {
|
}: {
|
||||||
path?: Href;
|
path?: Href;
|
||||||
icon?: React.ReactNode | any;
|
icon?: React.ReactNode | any;
|
||||||
iconCustom?: React.ReactNode;
|
iconCustom?: React.ReactNode;
|
||||||
|
onPress?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -26,7 +28,7 @@ const LeftButtonCustom = ({
|
|||||||
name={icon}
|
name={icon}
|
||||||
size={20}
|
size={20}
|
||||||
color={MainColor.yellow}
|
color={MainColor.yellow}
|
||||||
onPress={() => (path ? router.replace(path) : router.back())}
|
onPress={() => (onPress ? onPress() : path ? router.replace(path) : router.back())}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const funUpdateStatusJob = async ({
|
|||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
changeStatus: "publish" | "review" | "reject";
|
changeStatus: "publish" | "review" | "reject";
|
||||||
data?: string;
|
data?: any;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
const response = await apiAdminJobUpdate({
|
const response = await apiAdminJobUpdate({
|
||||||
|
|||||||
Reference in New Issue
Block a user