initial commit

This commit is contained in:
ferdiansyah783
2025-08-05 12:35:40 +07:00
commit fffa2ead5c
1069 changed files with 118056 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
'use client'
// React Imports
import type { ReactNode } from 'react'
// Third-party Imports
import { Provider } from 'react-redux'
import { store } from '@/redux-store'
const ReduxProvider = ({ children }: { children: ReactNode }) => {
return <Provider store={store}>{children}</Provider>
}
export default ReduxProvider
+21
View File
@@ -0,0 +1,21 @@
// Third-party Imports
import { configureStore } from '@reduxjs/toolkit'
// Slice Imports
import chatReducer from '@/redux-store/slices/chat'
import calendarReducer from '@/redux-store/slices/calendar'
import kanbanReducer from '@/redux-store/slices/kanban'
import emailReducer from '@/redux-store/slices/email'
export const store = configureStore({
reducer: {
chatReducer,
calendarReducer,
kanbanReducer,
emailReducer
},
middleware: getDefaultMiddleware => getDefaultMiddleware({ serializableCheck: false })
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
+94
View File
@@ -0,0 +1,94 @@
// Third-party Imports
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
import type { EventInput } from '@fullcalendar/core'
// Type Imports
import type { CalendarFiltersType, CalendarType } from '@/types/apps/calendarTypes'
// Data Imports
import { events } from '@/fake-db/apps/calendar'
const initialState: CalendarType = {
events: events,
filteredEvents: events,
selectedEvent: null,
selectedCalendars: ['Personal', 'Business', 'Family', 'Holiday', 'ETC']
}
const filterEventsUsingCheckbox = (events: EventInput[], selectedCalendars: CalendarFiltersType[]) => {
return events.filter(event => selectedCalendars.includes(event.extendedProps?.calendar as CalendarFiltersType))
}
export const calendarSlice = createSlice({
name: 'calendar',
initialState: initialState,
reducers: {
filterEvents: state => {
state.filteredEvents = state.events
},
addEvent: (state, action) => {
const newEvent = { ...action.payload, id: `${parseInt(state.events[state.events.length - 1]?.id ?? '') + 1}` }
state.events.push(newEvent)
},
updateEvent: (state, action: PayloadAction<EventInput>) => {
state.events = state.events.map(event => {
if (action.payload._def && event.id === action.payload._def.publicId) {
return {
id: event.id,
url: action.payload._def.url,
title: action.payload._def.title,
allDay: action.payload._def.allDay,
end: action.payload._instance.range.end,
start: action.payload._instance.range.start,
extendedProps: action.payload._def.extendedProps
}
} else if (event.id === action.payload.id) {
return action.payload
} else {
return event
}
})
},
deleteEvent: (state, action) => {
state.events = state.events.filter(event => event.id !== action.payload)
},
selectedEvent: (state, action) => {
state.selectedEvent = action.payload
},
filterCalendarLabel: (state, action) => {
const index = state.selectedCalendars.indexOf(action.payload)
if (index !== -1) {
state.selectedCalendars.splice(index, 1)
} else {
state.selectedCalendars.push(action.payload)
}
state.events = filterEventsUsingCheckbox(state.filteredEvents, state.selectedCalendars)
},
filterAllCalendarLabels: (state, action) => {
state.selectedCalendars = action.payload ? ['Personal', 'Business', 'Family', 'Holiday', 'ETC'] : []
state.events = filterEventsUsingCheckbox(state.filteredEvents, state.selectedCalendars)
}
}
})
export const {
filterEvents,
addEvent,
updateEvent,
deleteEvent,
selectedEvent,
filterCalendarLabel,
filterAllCalendarLabels
} = calendarSlice.actions
export default calendarSlice.reducer
+80
View File
@@ -0,0 +1,80 @@
// Third-party Imports
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
// Type Imports
import type { StatusType } from '@/types/apps/chatTypes'
// Data Imports
import { db } from '@/fake-db/apps/chat'
export const chatSlice = createSlice({
name: 'chat',
initialState: db,
reducers: {
getActiveUserData: (state, action: PayloadAction<number>) => {
const activeUser = state.contacts.find(user => user.id === action.payload)
const chat = state.chats.find(chat => chat.userId === action.payload)
if (chat && chat.unseenMsgs > 0) {
chat.unseenMsgs = 0
}
if (activeUser) {
state.activeUser = activeUser
}
},
addNewChat: (state, action) => {
const { id } = action.payload
state.contacts.find(contact => {
if (contact.id === id && !state.chats.find(chat => chat.userId === contact.id)) {
state.chats.unshift({
id: state.chats.length + 1,
userId: contact.id,
unseenMsgs: 0,
chat: []
})
}
})
},
setUserStatus: (state, action: PayloadAction<{ status: StatusType }>) => {
state.profileUser = {
...state.profileUser,
status: action.payload.status
}
},
sendMsg: (state, action: PayloadAction<{ msg: string }>) => {
const { msg } = action.payload
const existingChat = state.chats.find(chat => chat.userId === state.activeUser?.id)
if (existingChat) {
existingChat.chat.push({
message: msg,
time: new Date(),
senderId: state.profileUser.id,
msgStatus: {
isSent: true,
isDelivered: false,
isSeen: false
}
})
// Remove the chat from its current position
state.chats = state.chats.filter(chat => chat.userId !== state.activeUser?.id)
// Add the chat back to the beginning of the array
state.chats.unshift(existingChat)
}
}
}
})
export const { getActiveUserData, addNewChat, setUserStatus, sendMsg } = chatSlice.actions
export default chatSlice.reducer
+140
View File
@@ -0,0 +1,140 @@
// Third-party Imports
import { createSlice } from '@reduxjs/toolkit'
// Type Imports
import type { Email, EmailState } from '@/types/apps/emailTypes'
// Data Imports
import { db } from '@/fake-db/apps/email'
// Constants
const initialState: EmailState = {
emails: db.emails,
filteredEmails: []
}
export const emailSlice = createSlice({
name: 'email',
initialState,
reducers: {
// Filter all emails based on folder and label
filterEmails: (state, action) => {
const { emails, folder, label, uniqueLabels } = action.payload
state.filteredEmails = emails.filter((email: Email) => {
if (folder === 'starred' && email.folder !== 'trash') {
return email.isStarred
} else if (uniqueLabels.includes(label) && email.folder !== 'trash') {
return email.labels.includes(label)
} else {
return email.folder === folder
}
})
},
// Move all selected emails to folder
moveEmailsToFolder: (state, action) => {
const { emailIds, folder } = action.payload
state.emails = state.emails.map(email => {
return emailIds.includes(email.id) ? { ...email, folder } : email
})
},
// Delete all selected emails from trash
deleteTrashEmails: (state, action) => {
const { emailIds } = action.payload
state.emails = state.emails.filter(email => !emailIds.includes(email.id))
},
// Toggle read/unread status of all selected emails
toggleReadEmails: (state, action) => {
const { emailIds } = action.payload
const doesContainUnread = state.filteredEmails
.filter(email => emailIds.includes(email.id))
.some(email => !email.isRead)
const areAllUnread = state.filteredEmails
.filter(email => emailIds.includes(email.id))
.every(email => !email.isRead)
const areAllRead = state.filteredEmails.filter(email => emailIds.includes(email.id)).every(email => email.isRead)
state.emails = state.emails.map(email => {
if (emailIds.includes(email.id) && (doesContainUnread || areAllUnread)) {
return { ...email, isRead: true }
} else if (emailIds.includes(email.id) && areAllRead) {
return { ...email, isRead: false }
}
return email
})
},
// Toggle label to all selected emails
toggleLabel: (state, action) => {
const { emailIds, label } = action.payload
state.emails = state.emails.map(email => {
if (emailIds.includes(email.id)) {
return email.labels.includes(label)
? { ...email, labels: email.labels.filter(l => l !== label) }
: { ...email, labels: [...email.labels, label] }
}
return email
})
},
// Toggle starred status of email
toggleStarEmail: (state, action) => {
const { emailId } = action.payload
state.emails = state.emails.map(email => {
return email.id === emailId ? { ...email, isStarred: !email.isStarred } : email
})
},
// Get current email and mark it as read
getCurrentEmail: (state, action) => {
state.currentEmailId = action.payload
state.emails = state.emails.map(email => {
return email.id === action.payload && !email.isRead ? { ...email, isRead: true } : email
})
},
// Navigate to next or previous email
navigateEmails: (state, action) => {
const { type, emails: filteredEmails, currentEmailId } = action.payload
const currentIndex = filteredEmails.findIndex((email: Email) => email.id === currentEmailId)
if (type === 'next' && currentIndex < filteredEmails.length - 1) {
state.currentEmailId = filteredEmails[currentIndex + 1].id
} else if (type === 'prev' && currentIndex > 0) {
state.currentEmailId = filteredEmails[currentIndex - 1].id
}
// Mark email as read on navigation
if (state.currentEmailId) {
state.emails.filter(email => email.id === state.currentEmailId)[0].isRead = true
}
}
}
})
export const {
filterEmails,
moveEmailsToFolder,
deleteTrashEmails,
toggleReadEmails,
toggleLabel,
toggleStarEmail,
getCurrentEmail,
navigateEmails
} = emailSlice.actions
export default emailSlice.reducer
+124
View File
@@ -0,0 +1,124 @@
// Third-party Imports
import { createSlice } from '@reduxjs/toolkit'
// Type Imports
import type { ColumnType, TaskType } from '@/types/apps/kanbanTypes'
// Data Imports
import { db } from '@/fake-db/apps/kanban'
export const kanbanSlice = createSlice({
name: 'kanban',
initialState: db,
reducers: {
addColumn: (state, action) => {
const maxId = Math.max(...state.columns.map(column => column.id))
const newColumn: ColumnType = {
id: maxId + 1,
title: action.payload,
taskIds: []
}
state.columns.push(newColumn)
},
editColumn: (state, action) => {
const { id, title } = action.payload
const column = state.columns.find(column => column.id === id)
if (column) {
column.title = title
}
},
deleteColumn: (state, action) => {
const { columnId } = action.payload
const column = state.columns.find(column => column.id === columnId)
state.columns = state.columns.filter(column => column.id !== columnId)
if (column) {
state.tasks = state.tasks.filter(task => !column.taskIds.includes(task.id))
}
},
updateColumns: (state, action) => {
state.columns = action.payload
},
updateColumnTaskIds: (state, action) => {
const { id, tasksList } = action.payload
state.columns = state.columns.map(column => {
if (column.id === id) {
return { ...column, taskIds: tasksList.map((task: TaskType) => task.id) }
}
return column
})
},
addTask: (state, action) => {
const { columnId, title } = action.payload
const newTask: TaskType = {
id: state.tasks[state.tasks.length - 1].id + 1,
title
}
const column = state.columns.find(column => column.id === columnId)
if (column) {
column.taskIds.push(newTask.id)
}
state.tasks.push(newTask)
return state
},
editTask: (state, action) => {
const { id, title, badgeText, dueDate } = action.payload
const task = state.tasks.find(task => task.id === id)
if (task) {
task.title = title
task.badgeText = badgeText
task.dueDate = dueDate
}
},
deleteTask: (state, action) => {
const taskId = action.payload
state.tasks = state.tasks.filter(task => task.id !== taskId)
state.columns = state.columns.map(column => {
return {
...column,
taskIds: column.taskIds.filter(id => id !== taskId)
}
})
},
getCurrentTask: (state, action) => {
state.currentTaskId = action.payload
}
}
})
export const {
addColumn,
editColumn,
deleteColumn,
updateColumns,
updateColumnTaskIds,
addTask,
editTask,
deleteTask,
getCurrentTask
} = kanbanSlice.actions
export default kanbanSlice.reducer