project setup
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { Text, View, Pressable, ScrollView } from "react-native";
|
||||
import { router } from "expo-router";
|
||||
|
||||
import { useAuthContext } from "@/auth/AuthContext";
|
||||
import { ToDoList } from "@/components/features/dashboard/todo/ToDoList";
|
||||
import { LoadingSpinner } from "@/components/ui/LoadingSpinner";
|
||||
import { RiskScoreIndicator } from "@/components/ui/RiskScoreIndicator";
|
||||
import { useGetTodosQuery } from "@/store/api/todosApi";
|
||||
import { useAppSelector } from "@/store/hooks";
|
||||
import { selectIsMobile } from "@/store/screen/screenSizeSlice";
|
||||
import { RoleEnum } from "@/constants/roleEnum";
|
||||
import { NoData } from "@/components/ui/NoData";
|
||||
|
||||
const Dashboard = () => {
|
||||
const { user } = useAuthContext();
|
||||
const isMobile = useAppSelector(selectIsMobile);
|
||||
const isManager = user?.role === RoleEnum.MANAGER;
|
||||
|
||||
const queryArgs = isManager ? undefined : { assigneeUserId: user?.id };
|
||||
|
||||
const { data, error, refetch, isLoading, isFetching } =
|
||||
useGetTodosQuery(queryArgs);
|
||||
|
||||
const allTodos = data?.items ?? [];
|
||||
const teamTodos = isManager ? allTodos : [];
|
||||
const myTodos = isManager
|
||||
? allTodos.filter((t) => t.assignees?.some((a) => a.id === user?.id))
|
||||
: allTodos;
|
||||
return (
|
||||
<View
|
||||
className={`flex-1 bg-primaryPurpleLight ${isMobile ? "p-1" : "p-5"}`}
|
||||
>
|
||||
<View className="p-0">
|
||||
<Text
|
||||
className={`text-2xl font-bold ${
|
||||
isMobile ? "mb-1" : "mb-4"
|
||||
} text-white`}
|
||||
>
|
||||
User dashboard
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<ScrollView>
|
||||
<View className="flex flex-col lg:flex-row flex-1">
|
||||
<View className="w-full lg:w-1/2 lg:pr-2 mb-4 lg:mb-0">
|
||||
<View className="bg-lightGray rounded-lg p-4 mb-4">
|
||||
<Text className="text-xl font-bold mb-4 text-black">
|
||||
Risk Score Indicators
|
||||
</Text>
|
||||
<View className="flex flex-row flex-wrap justify-around gap-4">
|
||||
<RiskScoreIndicator
|
||||
value={100}
|
||||
subtitle="MFA setup"
|
||||
size={110}
|
||||
/>
|
||||
<RiskScoreIndicator
|
||||
value={50}
|
||||
subtitle="Fishing emails"
|
||||
size={110}
|
||||
/>
|
||||
<RiskScoreIndicator
|
||||
value={25}
|
||||
subtitle="Login location change"
|
||||
size={110}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="bg-lightGray rounded-lg p-4 mb-4">
|
||||
<Text className="text-xl font-bold mb-4 text-black">
|
||||
User Report (High Risk)
|
||||
</Text>
|
||||
<Text className="font-normal text-black">Some high risk</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="w-full lg:w-1/2 lg:pl-2 flex-1">
|
||||
{isLoading ? (
|
||||
<View className="bg-lightGray rounded-lg p-4 flex-1 items-center justify-center">
|
||||
<LoadingSpinner />
|
||||
</View>
|
||||
) : error ? (
|
||||
<View className="bg-lightGray rounded-lg p-4">
|
||||
<NoData
|
||||
onRetry={refetch}
|
||||
message="No todos found"
|
||||
isRetrying={isFetching}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<View>
|
||||
<View className="bg-lightGray rounded-lg p-4 mb-4">
|
||||
<Pressable
|
||||
onPress={() => router.push("/(tabs)/accounts/todos")}
|
||||
>
|
||||
<Text className="text-xl font-bold mb-4 text-black">
|
||||
Your To-Do List
|
||||
</Text>
|
||||
</Pressable>
|
||||
<ToDoList
|
||||
todos={myTodos}
|
||||
emptyMessage="No tasks assigned to you"
|
||||
/>
|
||||
</View>
|
||||
|
||||
{user?.role === RoleEnum.MANAGER && (
|
||||
<View className="bg-lightGray rounded-lg p-4 mb-4">
|
||||
<Pressable
|
||||
onPress={() => router.push("/(tabs)/accounts/todos")}
|
||||
>
|
||||
<Text className="text-xl font-bold mb-4 text-black">
|
||||
Your Team To-Do List
|
||||
</Text>
|
||||
</Pressable>
|
||||
|
||||
<ToDoList
|
||||
todos={teamTodos}
|
||||
emptyMessage="No tasks assigned to your team"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
@@ -0,0 +1,45 @@
|
||||
import { View, Pressable } from "react-native";
|
||||
import { Text } from "react-native-paper";
|
||||
|
||||
import { Todo } from "@/store/models/Todo.model";
|
||||
import { PriorityIndicator } from "@/components/ui/PriorityIndicator";
|
||||
import { SizeEnum } from "@/constants/sizeEnum";
|
||||
import { StatusTableItem } from "@/components/ui/StatusTableItem";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
interface DesktopTodoListItemProps {
|
||||
item: Todo;
|
||||
dialogOpen: boolean;
|
||||
setDialogOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const DesktopTodoListItem = ({
|
||||
item,
|
||||
dialogOpen,
|
||||
setDialogOpen,
|
||||
}: DesktopTodoListItemProps) => {
|
||||
return (
|
||||
<View className="flex-1">
|
||||
<View className="flex-row items-center">
|
||||
<Text className="text-base font-medium flex-1 mr-2" numberOfLines={1}>
|
||||
{item.title || item.task}
|
||||
</Text>
|
||||
<View className="flex-row items-center gap-2">
|
||||
<StatusTableItem todoStatus={item.status} />
|
||||
<PriorityIndicator priority={item.priority} size={SizeEnum.Small} />
|
||||
</View>
|
||||
<Pressable onPress={() => setDialogOpen(true)}>
|
||||
<Ionicons
|
||||
name="eye-outline"
|
||||
size={20}
|
||||
color="black"
|
||||
style={{ marginLeft: 5 }}
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
<Text className="text-sm text-gray-700 mt-0.5" numberOfLines={2}>
|
||||
{item.task}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Pressable, View } from "react-native";
|
||||
import { Badge, Text } from "react-native-paper";
|
||||
|
||||
import { SizeEnum } from "@/constants/sizeEnum";
|
||||
import { TodoStatusColors } from "@/constants/todoStatusEnum";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Todo } from "@/store/models/Todo.model";
|
||||
import { PriorityIndicator } from "@/components/ui/PriorityIndicator";
|
||||
|
||||
interface MobileTodoListItemProps {
|
||||
item: Todo;
|
||||
dialogOpen: boolean;
|
||||
setDialogOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const MobileTodoListItem = ({
|
||||
item,
|
||||
dialogOpen,
|
||||
setDialogOpen,
|
||||
}: MobileTodoListItemProps) => {
|
||||
return (
|
||||
<View className="flex-1">
|
||||
<View className="flex-row items-center mb-2">
|
||||
<View className="mr-2">
|
||||
<Badge
|
||||
size={12}
|
||||
style={{ backgroundColor: TodoStatusColors[item.status] }}
|
||||
/>
|
||||
</View>
|
||||
<Text className="font-bold flex-1 flex-wrap" numberOfLines={0}>
|
||||
{item.title}
|
||||
</Text>
|
||||
<View className="ml-2">
|
||||
<PriorityIndicator priority={item.priority} size={SizeEnum.Small} />
|
||||
</View>
|
||||
<Pressable onPress={() => setDialogOpen(true)}>
|
||||
<Ionicons
|
||||
name="eye-outline"
|
||||
size={20}
|
||||
color="black"
|
||||
style={{ marginLeft: 5 }}
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
<Text className="text-sm text-gray-600" numberOfLines={2}>
|
||||
{item.task}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import { FlatList, Text, View } from "react-native";
|
||||
|
||||
import { ToDoListItem } from "@/components/features/dashboard/todo/ToDoListItem";
|
||||
import { Todo } from "@/store/models/Todo.model";
|
||||
|
||||
interface ToDoListProps {
|
||||
todos: Todo[];
|
||||
emptyMessage?: string;
|
||||
}
|
||||
|
||||
export const ToDoList = ({ todos, emptyMessage }: ToDoListProps) => {
|
||||
if (todos?.length === 0) {
|
||||
return (
|
||||
<View className="flex-1 justify-center items-center py-10">
|
||||
<Text className="mt-2">{emptyMessage}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={todos}
|
||||
renderItem={({ item }) => <ToDoListItem item={item} />}
|
||||
keyExtractor={(item) => item.id}
|
||||
contentContainerStyle={{ paddingBottom: 20 }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
import { Todo } from "@/store/models/Todo.model";
|
||||
import { useAppSelector } from "@/store/hooks";
|
||||
import { selectIsMobile } from "@/store/screen/screenSizeSlice";
|
||||
import { TodoDetailsModal } from "@/components/features/todos/TodoDetailsModal";
|
||||
import { MobileTodoListItem } from "@/components/features/dashboard/todo/MobileTodoListItem";
|
||||
import { DesktopTodoListItem } from "@/components/features/dashboard/todo/DesktopTodoListItem";
|
||||
|
||||
interface ToDoListItemProps {
|
||||
item: Todo;
|
||||
}
|
||||
|
||||
export const ToDoListItem = ({ item }: ToDoListItemProps) => {
|
||||
const isMobile = useAppSelector(selectIsMobile);
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<View className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
||||
<View className="flex-row items-center justify-between mb-2">
|
||||
{isMobile ? (
|
||||
<MobileTodoListItem
|
||||
dialogOpen={dialogOpen}
|
||||
setDialogOpen={setDialogOpen}
|
||||
item={item}
|
||||
/>
|
||||
) : (
|
||||
<DesktopTodoListItem
|
||||
dialogOpen={dialogOpen}
|
||||
setDialogOpen={setDialogOpen}
|
||||
item={item}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<TodoDetailsModal
|
||||
visible={dialogOpen}
|
||||
todo={item}
|
||||
onClose={() => setDialogOpen(false)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user