diff --git a/src/app/(main)/home/page.tsx b/src/app/(main)/home/page.tsx index 1901092..3d95845 100644 --- a/src/app/(main)/home/page.tsx +++ b/src/app/(main)/home/page.tsx @@ -1,11 +1,17 @@ import { Metadata } from "next"; +import { I18nProvider } from "@/i18n"; +import UserWorkspacePage from "@/components/UserWorkspacePage/UserWorkspacePage"; export const metadata: Metadata = { - title: "Home", + title: "User Workspace", }; const Home: React.FC = async () => { - return "Home"; + return ( + + + + ); }; export default Home; diff --git a/src/components/AssignedProjectGrid/AssignedProjectGrid.tsx b/src/components/AssignedProjectGrid/AssignedProjectGrid.tsx new file mode 100644 index 0000000..fa5d7b2 --- /dev/null +++ b/src/components/AssignedProjectGrid/AssignedProjectGrid.tsx @@ -0,0 +1,176 @@ +import * as React from 'react'; +import { Card, CardHeader, CardContent, SxProps, Theme, Tabs, Tab, Box, Typography, Grid, Link} from '@mui/material'; +import { DataGrid, GridColDef } from '@mui/x-data-grid'; +import { darken, lighten, styled } from '@mui/material/styles'; +import { ThemeProvider } from '@emotion/react'; +import { TAB_THEME } from '@/theme/colorConst'; +import AllProjectGrid from '../UserWorkspacePage/ProjectGrid'; + + +interface AssignedProjectGridProps { + Title?: string; + // rows: any[]; + // columns: any[]; + columnWidth?: number; + Style?: boolean; + sx?: SxProps; + height?: number; + [key: string]: any; +} + +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; +} + +function CustomTabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: number) { + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, + }; +} + +const AssignedProjectGrid: React.FC = ({ + Title, + rows, + columns, + columnWidth, + Style = true, + sx, + height, + ...props +}) => { + // const modifiedColumns = columns.map((column) => { + // return { + // ...column, + // width: columnWidth ?? 150, + // }; + // }); + + // const rowsWithDefaultValues = rows.map((row) => { + // return { ...row }; + // }); + + const getBackgroundColor = (color: string, mode: 'light' | 'dark') => + mode === 'dark' ? darken(color, 0.7) : lighten(color, 0.7); + + const getHoverBackgroundColor = (color: string, mode: 'light' | 'dark') => + mode === 'dark' ? darken(color, 0.6) : lighten(color, 0.6); + + const getSelectedBackgroundColor = (color: string, mode: 'light' | 'dark') => + mode === 'dark' ? darken(color, 0.5) : lighten(color, 0.5); + + const getSelectedHoverBackgroundColor = (color: string, mode: 'light' | 'dark') => + mode === 'dark' ? darken(color, 0.4) : lighten(color, 0.4); + + const StyledDataGrid = styled(DataGrid)(({ theme }) => ({ + '& .super-app-theme--Open': { + backgroundColor: getBackgroundColor(theme.palette.info.main, theme.palette.mode), + '&:hover': { + backgroundColor: getHoverBackgroundColor(theme.palette.info.main, theme.palette.mode) + }, + '&.Mui-selected': { + backgroundColor: getSelectedBackgroundColor(theme.palette.info.main, theme.palette.mode), + '&:hover': { + backgroundColor: getSelectedHoverBackgroundColor(theme.palette.info.main, theme.palette.mode) + } + } + }, + '& .super-app-theme--finish': { + backgroundColor: getBackgroundColor(theme.palette.success.main, theme.palette.mode), + '&:hover': { + backgroundColor: getHoverBackgroundColor(theme.palette.success.main, theme.palette.mode) + }, + '&.Mui-selected': { + backgroundColor: getSelectedBackgroundColor(theme.palette.success.main, theme.palette.mode), + '&:hover': { + backgroundColor: getSelectedHoverBackgroundColor(theme.palette.success.main, theme.palette.mode) + } + } + }, + '& .super-app-theme--danger': { + backgroundColor: getBackgroundColor(theme.palette.warning.main, theme.palette.mode), + '&:hover': { + backgroundColor: getHoverBackgroundColor(theme.palette.warning.main, theme.palette.mode) + }, + '&.Mui-selected': { + backgroundColor: getSelectedBackgroundColor(theme.palette.warning.main, theme.palette.mode), + '&:hover': { + backgroundColor: getSelectedHoverBackgroundColor(theme.palette.warning.main, theme.palette.mode) + } + } + }, + '& .super-app-theme--warning': { + backgroundColor: getBackgroundColor(theme.palette.error.main, theme.palette.mode), + '&:hover': { + backgroundColor: getHoverBackgroundColor(theme.palette.error.main, theme.palette.mode) + }, + '&.Mui-selected': { + backgroundColor: getSelectedBackgroundColor(theme.palette.error.main, theme.palette.mode), + '&:hover': { + backgroundColor: getSelectedHoverBackgroundColor(theme.palette.error.main, theme.palette.mode) + } + } + } + })); + + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.SyntheticEvent, newValue: number) => { + setValue(newValue); + }; + + return ( +
+ + {Title && } + +
+ + + + + + + + + {/* + Item {value} + + + Item {value} + + + Item {value} + */} + +
+
+ +
+
+ ); +}; + +export default AssignedProjectGrid; diff --git a/src/components/AssignedProjectGrid/index.ts b/src/components/AssignedProjectGrid/index.ts new file mode 100644 index 0000000..7192046 --- /dev/null +++ b/src/components/AssignedProjectGrid/index.ts @@ -0,0 +1 @@ +export { default } from "./AssignedProjectGrid"; diff --git a/src/components/CustomCardGrid/CustomCardGrid.tsx b/src/components/CustomCardGrid/CustomCardGrid.tsx new file mode 100644 index 0000000..de4ddd6 --- /dev/null +++ b/src/components/CustomCardGrid/CustomCardGrid.tsx @@ -0,0 +1,171 @@ +import * as React from 'react'; +import { Card, CardHeader, CardContent, SxProps, Theme, Grid } from '@mui/material'; +import { DataGrid, GridColDef } from '@mui/x-data-grid'; +import { darken, lighten, styled } from '@mui/material/styles'; +import { PROJECT_CARD_STYLE } from '@/theme/colorConst'; +import { useRef, useEffect, useState } from 'react'; +import Swal from 'sweetalert2'; +import styledcmp from 'styled-components'; + +const CardWrapper = styledcmp.div` + /* Styles for the card when not hovered */ + background-color: #f0f0f0; + padding: 10px; + /* ...other styles... */ + + &:hover { + /* Styles for the card when hovered */ + background-color: #c0c0c0; + /* ...other hover styles... */ + } +`; + +interface CustomCardGridProps { + Title?: string; + cardsPerRow?: number; + rows?: any[]; + columns?: any[]; + items: any[]; + columnWidth?: number; + Style?: boolean; + sx?: SxProps; + dataGridHeight?: number; + cardStyle?: any; + [key: string]: any; +} + +const CustomCardGrid: React.FC = ({ + Title, + rows, + items, + columns, + columnWidth, + cardsPerRow = 4, + Style = true, + sx, + dataGridHeight, + ...props +}) => { + const getBackgroundColor = (color: string, mode: 'light' | 'dark') => + mode === 'dark' ? darken(color, 0.7) : lighten(color, 0.7); + + const getHoverBackgroundColor = (color: string, mode: 'light' | 'dark') => + mode === 'dark' ? darken(color, 0.6) : lighten(color, 0.6); + + const getSelectedBackgroundColor = (color: string, mode: 'light' | 'dark') => + mode === 'dark' ? darken(color, 0.5) : lighten(color, 0.5); + + const getSelectedHoverBackgroundColor = (color: string, mode: 'light' | 'dark') => + mode === 'dark' ? darken(color, 0.4) : lighten(color, 0.4); + + const StyledCard = styled(Card)(({ theme }) => ({ + '& .super-app-theme--Open': { + backgroundColor: getBackgroundColor(theme.palette.info.main, theme.palette.mode), + '&:hover': { + backgroundColor: getHoverBackgroundColor(theme.palette.info.main, theme.palette.mode) + }, + '&.Mui-selected': { + backgroundColor: getSelectedBackgroundColor(theme.palette.info.main, theme.palette.mode), + '&:hover': { + backgroundColor: getSelectedHoverBackgroundColor(theme.palette.info.main, theme.palette.mode) + } + } + }, + '& .super-app-theme--finish': { + backgroundColor: getBackgroundColor(theme.palette.success.main, theme.palette.mode), + '&:hover': { + backgroundColor: getHoverBackgroundColor(theme.palette.success.main, theme.palette.mode) + }, + '&.Mui-selected': { + backgroundColor: getSelectedBackgroundColor(theme.palette.success.main, theme.palette.mode), + '&:hover': { + backgroundColor: getSelectedHoverBackgroundColor(theme.palette.success.main, theme.palette.mode) + } + } + }, + '& .super-app-theme--danger': { + backgroundColor: getBackgroundColor(theme.palette.warning.main, theme.palette.mode), + '&:hover': { + backgroundColor: getHoverBackgroundColor(theme.palette.warning.main, theme.palette.mode) + }, + '&.Mui-selected': { + backgroundColor: getSelectedBackgroundColor(theme.palette.warning.main, theme.palette.mode), + '&:hover': { + backgroundColor: getSelectedHoverBackgroundColor(theme.palette.warning.main, theme.palette.mode) + } + } + }, + '& .super-app-theme--warning': { + backgroundColor: getBackgroundColor(theme.palette.error.main, theme.palette.mode), + '&:hover': { + backgroundColor: getHoverBackgroundColor(theme.palette.error.main, theme.palette.mode) + }, + '&.Mui-selected': { + backgroundColor: getSelectedBackgroundColor(theme.palette.error.main, theme.palette.mode), + '&:hover': { + backgroundColor: getSelectedHoverBackgroundColor(theme.palette.error.main, theme.palette.mode) + } + } + } + })); + + const CardItem = (item: any) => { + const cardItem = item.item as Record; + return props.cardStyle?? ( + // + + + {Object.keys(cardItem).map((key) => ( +

+ {key}: {cardItem[key]} +

+ ))} +
+
+ //
+ ); + }; + + const containerRef = useRef(null!); + + const [cardMargin, setCardMargin] = useState(1.5); + + useEffect(() => { + console.log(CardItem); + const resizeHandler = () => { + const containerWidth = containerRef.current.offsetWidth; + const cardCount = items.length; + const rootSize = parseFloat(getComputedStyle(document.documentElement).fontSize); + setCardMargin((containerWidth - cardsPerRow * (rootSize * parseInt(PROJECT_CARD_STYLE.width.slice(0, -3),10))) /(2 * cardsPerRow)); + // Set the cardMargin value using style={{margin: `${cardMargin}px`, ...PROJECT_CARD_STYLE}} + }; + + window.addEventListener('resize', resizeHandler); + + resizeHandler(); // Initial calculation + + // Swal.fire({ + // title: 'Error! ', + // text: `Card Count is ${items.length}`, + // icon: 'success', + // confirmButtonText: 'Jus Cool' + // }) + + return () => { + window.removeEventListener('resize', resizeHandler); + }; + }, [items]); + + return ( +
+ {/*

width is {containerRef.current == null? "idk":containerRef.current.offsetWidth}, margin is {cardMargin}

*/} + {items.map((item, index) => ( +
+ {props.cardStyle? props.cardStyle(item) : } +
+ ))} +
+ ); +}; + +export default CustomCardGrid; diff --git a/src/components/CustomCardGrid/index.ts b/src/components/CustomCardGrid/index.ts new file mode 100644 index 0000000..b051fef --- /dev/null +++ b/src/components/CustomCardGrid/index.ts @@ -0,0 +1 @@ +export { default } from "./CustomCardGrid"; diff --git a/src/components/CustomModal/CustomModal.tsx b/src/components/CustomModal/CustomModal.tsx new file mode 100644 index 0000000..97eb047 --- /dev/null +++ b/src/components/CustomModal/CustomModal.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import { Card, CardHeader, CardContent, SxProps, Theme, Grid, Modal, Typography, Button } from '@mui/material'; +import { DataGrid, GridColDef } from '@mui/x-data-grid'; +import { darken, lighten, styled } from '@mui/material/styles'; +import { PROJECT_MODAL_STYLE } from '@/theme/colorConst'; +import { useRef, useEffect, useState } from 'react'; +import Swal from 'sweetalert2'; +import styledcmp from 'styled-components'; + +const CardWrapper = styledcmp.div` + /* Styles for the card when not hovered */ + background-color: #f0f0f0, + padding: 10px, + /* ...other styles... */ + + &:hover { + /* Styles for the card when hovered */ + background-color: #c0c0c0, + /* ...other hover styles... */ + } +`; + +interface CustomModalProps { + title?: string; + isOpen: boolean; + onClose: () => void; + modalStyle?: any; +} + +const CustomModal: React.FC = ({ ...props }) => { + + const ModalContent = () => { + return ( + // +
+ + {props.title??"Modal Title"} + + + Modal Content + +
+ + +
+
+ //
+ ); + }; + + return ( + + {props.modalStyle? : } + + ); +}; + +export default CustomModal; \ No newline at end of file diff --git a/src/components/CustomModal/index.ts b/src/components/CustomModal/index.ts new file mode 100644 index 0000000..f13c96c --- /dev/null +++ b/src/components/CustomModal/index.ts @@ -0,0 +1 @@ +export { default } from "./CustomModal"; diff --git a/src/components/EnterTimesheet/EnterTimesheetModal.tsx b/src/components/EnterTimesheet/EnterTimesheetModal.tsx new file mode 100644 index 0000000..4aa8023 --- /dev/null +++ b/src/components/EnterTimesheet/EnterTimesheetModal.tsx @@ -0,0 +1,80 @@ +"use client"; +import Grid from "@mui/material/Grid"; +import Paper from "@mui/material/Paper"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import AssignedProjectGrid from "../AssignedProjectGrid/AssignedProjectGrid"; +import PageTitle from "../PageTitle/PageTitle"; +import { Suspense } from "react"; +import Button from "@mui/material/Button"; +import Stack from "@mui/material/Stack"; +import { Add } from '@mui/icons-material'; +import Link from "next/link"; +import { t } from 'i18next'; +import { Modal, Typography } from "@mui/material"; +import CustomModal from "../CustomModal/CustomModal"; +import { PROJECT_MODAL_STYLE } from "@/theme/colorConst"; +import CustomDatagrid from "../CustomDatagrid/CustomDatagrid"; +import { DataGrid } from '@mui/x-data-grid'; +import TimesheetInputGrid from "./TimesheetInputGrid"; + +interface EnterTimesheetModalProps { + isOpen: boolean; + onClose: () => void; + modalStyle?: any; +} + +const EnterTimesheetModal: React.FC = ({ ...props }) => { + const [lockConfirm, setLockConfirm] = useState(false); + const columns = [ + { + id: 'projectCode', + field: 'projectCode', + headerName: "Project Code and Name", + flex: 1, + }, + { + id: 'task', + field: 'task', + headerName: "Task", + flex: 1, + }, + ]; + + const rows = [{ + id: 1, projectCode: "M1001", task: "1.2" + }, + { + id: 2, projectCode: "M1301", task: "1.1" + }]; + + return ( + +
+ +
+ Timesheet Input +
+
+ +
+ +
+ +
+ + +
+
+
+ ); +}; + +export default EnterTimesheetModal; diff --git a/src/components/EnterTimesheet/ProjectGrid.tsx b/src/components/EnterTimesheet/ProjectGrid.tsx new file mode 100644 index 0000000..ee06595 --- /dev/null +++ b/src/components/EnterTimesheet/ProjectGrid.tsx @@ -0,0 +1,62 @@ +"use client"; +import * as React from "react"; +import Grid from "@mui/material/Grid"; +import { useEffect, useState } from 'react' +import { TFunction } from "i18next"; +import { useTranslation } from "react-i18next"; +import {Card,CardContent,CardHeader} from '@mui/material'; +import CustomCardGrid from '../CustomCardGrid/CustomCardGrid'; +import '../../app/global.css'; +import { PROJECT_CARD_STYLE } from "@/theme/colorConst"; + +interface ProjectGridProps { + tab: number; +} + + +const ProjectGrid: React.FC = (props) => { + const [items, setItems] = React.useState([]) + + useEffect(() => { + if (props.tab == 0) { + setItems(cards) + } + else { + const filteredItems = cards;//cards.filter(item => (item.track == props.tab)); + setItems(filteredItems); + } + }, [props.tab]); + + const cards = [ + {code: 'M1001 (C)', name: 'Consultancy Project A', hr_spent: 12.75, hr_spent_normal: 0.00, hr_alloc: 150.00, hr_alloc_normal: 30.00}, + {code: 'M1301 (C)', name: 'Consultancy Project AAA', hr_spent: 4.25, hr_spent_normal: 0.25, hr_alloc: 30.00, hr_alloc_normal: 0.00}, + {code: 'M1354 (C)', name: 'Consultancy Project BBB', hr_spent: 57.00, hr_spent_normal: 6.50, hr_alloc: 100.00, hr_alloc_normal: 20.00}, + {code: 'M1973 (C)', name: 'Construction Project CCC', hr_spent: 12.75, hr_spent_normal: 0.00, hr_alloc: 150.00, hr_alloc_normal: 30.00}, + {code: 'M2014 (T)', name: 'Consultancy Project DDD', hr_spent: 1.00, hr_spent_normal: 0.00, hr_alloc: 10.00, hr_alloc_normal: 0.00}, + ]; + + const cardLayout = (item: Record) => { + return ( + + + +

Hours Spent: {item.hr_spent}

+

Normal (Others): {item.hr_spent_normal}

+

Hours Allocated: {item.hr_alloc}

+

Normal (Others): {item.hr_alloc_normal}

+
+
+ ); + } + // Apply the preset style to the cards in child, if not specified // + return ( + + {/* */} + {/* item count = {items?.length??"idk"} , track/tab = {props.tab} */} + + {/* */} + + ); +}; + +export default ProjectGrid; diff --git a/src/components/EnterTimesheet/TimesheetInputGrid.tsx b/src/components/EnterTimesheet/TimesheetInputGrid.tsx new file mode 100644 index 0000000..1ad4c0b --- /dev/null +++ b/src/components/EnterTimesheet/TimesheetInputGrid.tsx @@ -0,0 +1,502 @@ +"use client"; +import Grid from "@mui/material/Grid"; +import Paper from "@mui/material/Paper"; +import { useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import PageTitle from "../PageTitle/PageTitle"; +import { Suspense } from "react"; +import Button from "@mui/material/Button"; +import Stack from "@mui/material/Stack"; +import { Add, SettingsEthernet } from '@mui/icons-material'; +import Link from "next/link"; +import { t } from 'i18next'; +import { Box, Container, Modal, Select, SelectChangeEvent, Typography } from "@mui/material"; +import { Close } from '@mui/icons-material'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/DeleteOutlined'; +import SaveIcon from '@mui/icons-material/Save'; +import CancelIcon from '@mui/icons-material/Close'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import Swal from "sweetalert2"; +import { msg } from "../Swal/CustomAlerts"; +import ComboEditor from "../ComboEditor/ComboEditor"; +import React from "react"; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { + GridRowsProp, + GridRowModesModel, + GridRowModes, + DataGrid, + GridColDef, + GridToolbarContainer, + GridFooterContainer, + GridActionsCellItem, + GridEventListener, + GridRowId, + GridRowModel, + GridRowEditStopReasons, + GridEditInputCell, + GridValueSetterParams, +} from '@mui/x-data-grid'; +import { LocalizationProvider } from "@mui/x-date-pickers"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import dayjs from "dayjs"; +import { Props } from "react-intl/src/components/relative"; + +const weekdays = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']; + +interface BottomBarProps { + getHoursTotal: (column: string) => number; + setLockConfirm: (newLock: (oldLock: Boolean) => Boolean) => void; + setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void; + setRowModesModel: ( + newModel: (oldModel: GridRowModesModel) => GridRowModesModel, + ) => void; +} + +interface EditToolbarProps { + // setDay: (newDay : dayjs.Dayjs) => void; + setDay: (newDay: (oldDay: dayjs.Dayjs) => dayjs.Dayjs) => void; + setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void; + setRowModesModel: ( + newModel: (oldModel: GridRowModesModel) => GridRowModesModel, + ) => void; +} + +interface EditFooterProps { + setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void; + setRowModesModel: ( + newModel: (oldModel: GridRowModesModel) => GridRowModesModel, + ) => void; +} + +const BottomBar = (props: BottomBarProps) => { + const { setRows, setRowModesModel, getHoursTotal, setLockConfirm } = props; + // const getHoursTotal = props.getHoursTotal; + const [newId, setNewId] = useState(-1); + const [invalidDays, setInvalidDays] = useState(0); + + const handleAddClick = () => { + const id = newId; + setNewId(newId - 1); + setRows((oldRows) => [...oldRows, { id, projectCode: '', task: '', isNew: true }]); + setRowModesModel((oldModel) => ({ + ...oldModel, + [id]: { mode: GridRowModes.Edit, fieldToFocus: 'projectCode' }, + })); + }; + + const totalColDef = { + flex:1, + // style: {color:getHoursTotal('mon')>24?"red":"black"} + }; + + const TotalCell = ({value}: Props) => { + const [invalid, setInvalid] = useState(false); + + useEffect(()=> { + const newInvalid = (value??0)>24; + setInvalid(newInvalid); + }, [value]); + + return ( + + {value} + + ); + } + + const checkUnlockConfirmBtn = () => { + // setLockConfirm((oldLock)=> valid); + setLockConfirm((oldLock)=> weekdays.every(weekday => { + getHoursTotal(weekday) <= 24 + })); + } + + return ( +
+
+ + Total: + + + + + + + + +
+ +
+ ); +} + +const EditToolbar = (props: EditToolbarProps) => { + const { setDay } = props; + const [selectedDate, setSelectedDate] = useState(dayjs()); + + const handleClickLeft = () => { + if (selectedDate) { + const newDate = selectedDate.add(-7, 'day'); + setSelectedDate(newDate); + } + }; + const handleClickRight = () => { + if (selectedDate) { + const newDate = selectedDate.add(7, 'day') > dayjs()? dayjs(): selectedDate.add(7, 'day'); + setSelectedDate(newDate); + } + }; + + const handleDateChange = (date: dayjs.Dayjs | Date | null) => { + const newDate = dayjs(date); + setSelectedDate(newDate); + }; + + useEffect(() => { + setDay((oldDay) => selectedDate); + }, [selectedDate]); + + return ( + +
+ + + +
+
+ ); +} + +const EditFooter = (props: EditFooterProps) => { + return ( +
+ + Total: + + ssss +
+ ); +} + +interface TimesheetInputGridProps { + setLockConfirm: (newLock: (oldLock: Boolean) => Boolean) => void; + onClose?: () => void; +} + +const initialRows: GridRowsProp = [ + { + id: 1, + projectCode: "M1001", + task: "1.2", + mon: 2.5, + }, + { + id: 2, + projectCode: "M1002", + task: "1.3", + mon: 3.25, + }, +]; + +const options=["M1001", "M1301", "M1354", "M1973"]; +const options2=[ + "1.1 - Preparation of preliminary Cost Estimate / Cost Plan", + "1.2 - Cash flow forecast", + "1.3 - Cost studies fo alterative design solutions", + "1.4 = Attend design co-ordination / project review meetings", + "1.5 - Prepare / Review RIC"]; + +const getDateForHeader = (date : dayjs.Dayjs, weekday : number) => { + if (date.day() == 0) { + return date.add((weekday - date.day() - 7), 'day').format('DD MMM'); + } else { + return date.add(weekday - date.day(), 'day').format('DD MMM'); + } +} + +const TimesheetInputGrid: React.FC = ({ ...props }) => { + + const [rows, setRows] = useState(initialRows); + const [day, setDay] = useState(dayjs()); + const [rowModesModel, setRowModesModel] = React.useState({}); + const { setLockConfirm } = props; + + const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => { + if (params.reason === GridRowEditStopReasons.rowFocusOut) { + event.defaultMuiPrevented = true; + } + }; + + const handleEditClick = (id: GridRowId) => () => { + setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } }); + }; + + const handleSaveClick = (id: GridRowId) => () => { + + setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }); + }; + + const handleDeleteClick = (id: GridRowId) => () => { + setRows(rows.filter((row) => row.id !== id)); + }; + + const handleCancelClick = (id: GridRowId) => () => { + setRowModesModel({ + ...rowModesModel, + [id]: { mode: GridRowModes.View, ignoreModifications: true }, + }); + + const editedRow = rows.find((row) => row.id === id); + if (editedRow!.isNew) { + setRows(rows.filter((row) => row.id !== id)); + } + }; + + const processRowUpdate = (newRow: GridRowModel) => { + const updatedRow = { ...newRow, isNew: false }; + setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row))); + return updatedRow; + }; + + const handleRowModesModelChange = (newRowModesModel: GridRowModesModel) => { + setRowModesModel(newRowModesModel); + }; + + const getHoursTotal = (column : any) => { + let sum = 0; + rows.forEach((row) => { + sum += row[column]??0; + }); + return sum; + }; + + const weekdayColConfig : any = { + type: 'number', + // sortable: false, + //width: 100, + flex: 1, + align: 'left', + headerAlign: 'left', + editable: true, + renderEditCell: (value : any) => ( + + ), + }; + + const columns: GridColDef[] = [ + { + field: 'actions', + type: 'actions', + headerName: '', + width: 100, + cellClassName: 'actions', + getActions: ({ id }) => { + const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; + + if (isInEditMode) { + return [ + } + title="Save" + label="Save" + sx={{ + color: 'primary.main', + }} + onClick={handleSaveClick(id)} + />, + } + title="Cancel" + label="Cancel" + className="textPrimary" + onClick={handleCancelClick(id)} + color="inherit" + />, + ]; + } + + return [ + } + title="Edit" + label="Edit" + className="textPrimary" + onClick={handleEditClick(id)} + color="inherit" + />, + } + onClick={handleDeleteClick(id)} + sx={{color:"red"}} + />, + ]; + }, + }, + { + field: 'projectCode', + headerName: 'Project Code', + // width: 220, + flex: 2, + editable: true, + type: 'singleSelect', + valueOptions: options, + }, + { + field: 'task', + headerName: 'Task', + // width: 220, + flex: 3, + editable: true, + type: 'singleSelect', + valueOptions: options2, + }, + { + // Mon + field: 'mon', + ...weekdayColConfig, + renderHeader: () => { + return ( +
Mon - {getDateForHeader(day, 1)}
+ ); + }, + }, + { + // Tue + field: 'tue', + ...weekdayColConfig, + renderHeader: () => { + return ( +
Tue - {getDateForHeader(day, 2)}
+ ); + }, + }, + { + // Wed + field: 'wed', + ...weekdayColConfig, + renderHeader: () => { + return ( +
Wed - {getDateForHeader(day, 3)}
+ ); + }, + }, + { + // Thu + field: 'thu', + ...weekdayColConfig, + renderHeader: () => { + return ( +
Thu - {getDateForHeader(day, 4)}
+ ); + }, + }, + { + // Fri + field: 'fri', + ...weekdayColConfig, + renderHeader: () => { + return ( +
Fri - {getDateForHeader(day, 5)}
+ ); + }, + }, + { + // Sat + field: 'sat', + ...weekdayColConfig, + renderHeader: () => { + return ( +
Sat - {getDateForHeader(day, 6)}
+ ); + }, + }, + { + // Sun + field: 'sun', + ...weekdayColConfig, + renderHeader: () => { + return ( +
Sun - {getDateForHeader(day, 7)}
+ ); + }, + }, + // { + // field: 'joinDate', + // headerName: 'Join date', + // type: 'date', + // width: 180, + // editable: true, + // }, + ]; + + return ( + + + + + + ); +} + +export default TimesheetInputGrid; diff --git a/src/components/EnterTimesheet/index.ts b/src/components/EnterTimesheet/index.ts new file mode 100644 index 0000000..e070291 --- /dev/null +++ b/src/components/EnterTimesheet/index.ts @@ -0,0 +1 @@ +export { default } from "./EnterTimesheetModal"; diff --git a/src/components/Swal/CustomAlerts.js b/src/components/Swal/CustomAlerts.js new file mode 100644 index 0000000..d8d5cdd --- /dev/null +++ b/src/components/Swal/CustomAlerts.js @@ -0,0 +1,23 @@ +import Swal from "sweetalert2"; + + +export const msg = (text) => { + Swal.mixin({ + toast: true, + position: "bottom-end", + showConfirmButton: false, + timer: 3000, + timerProgressBar: true, + didOpen: (toast) => { + toast.onmouseenter = Swal.stopTimer; + toast.onmouseleave = Swal.resumeTimer; + } + }).fire({ + icon: "Success", + title: text + }); +} + +export const popup = (text) => { + Swal.fire(text); +} \ No newline at end of file diff --git a/src/components/UserWorkspacePage/ProjectGrid.tsx b/src/components/UserWorkspacePage/ProjectGrid.tsx new file mode 100644 index 0000000..53cc264 --- /dev/null +++ b/src/components/UserWorkspacePage/ProjectGrid.tsx @@ -0,0 +1,62 @@ +"use client"; +import * as React from "react"; +import Grid from "@mui/material/Grid"; +import { useEffect, useState } from 'react' +import { TFunction } from "i18next"; +import { useTranslation } from "react-i18next"; +import {Card,CardContent,CardHeader} from '@mui/material'; +import CustomCardGrid from '../CustomCardGrid/CustomCardGrid'; +import '../../app/global.css'; +import { PROJECT_CARD_STYLE } from "@/theme/colorConst"; + +interface ProjectGridProps { + tab: number; +} + + +const ProjectGrid: React.FC = (props) => { + const [items, setItems] = React.useState([]) + + useEffect(() => { + if (props.tab == 0) { + setItems(cards) + } + else { + const filteredItems = cards;//cards.filter(item => (item.track == props.tab)); + setItems(filteredItems); + } + }, [props.tab]); + + const cards = [ + {code: 'M1001 (C)', name: 'Consultancy Project A', hr_spent: 12.75, hr_spent_normal: 0.00, hr_alloc: 150.00, hr_alloc_normal: 30.00}, + {code: 'M1301 (C)', name: 'Consultancy Project AAA', hr_spent: 4.25, hr_spent_normal: 0.25, hr_alloc: 30.00, hr_alloc_normal: 0.00}, + {code: 'M1354 (C)', name: 'Consultancy Project BBB', hr_spent: 57.00, hr_spent_normal: 6.50, hr_alloc: 100.00, hr_alloc_normal: 20.00}, + {code: 'M1973 (C)', name: 'Construction Project CCC', hr_spent: 12.75, hr_spent_normal: 0.00, hr_alloc: 150.00, hr_alloc_normal: 30.00}, + {code: 'M2014 (T)', name: 'Consultancy Project DDD', hr_spent: 1.00, hr_spent_normal: 0.00, hr_alloc: 10.00, hr_alloc_normal: 0.00}, + ]; + + const cardLayout = (item: Record) => { + return ( + + + +

Hours Spent: {item.hr_spent}

+

Normal (Others): {item.hr_spent_normal}

+

Hours Allocated: {item.hr_alloc}

+

Normal (Others): {item.hr_alloc_normal}

+
+
+ ); + } + // Apply the preset style to the cards in child, if not specified // + return ( + + {/* */} + {/* item count = {items?.length??"idk"} , track/tab = {props.tab} */} + + {/* */} + + ); +}; + +export default ProjectGrid; diff --git a/src/components/UserWorkspacePage/UserWorkspacePage.tsx b/src/components/UserWorkspacePage/UserWorkspacePage.tsx new file mode 100644 index 0000000..19cd266 --- /dev/null +++ b/src/components/UserWorkspacePage/UserWorkspacePage.tsx @@ -0,0 +1,69 @@ +"use client"; +import Grid from "@mui/material/Grid"; +import Paper from "@mui/material/Paper"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import AssignedProjectGrid from "../AssignedProjectGrid/AssignedProjectGrid"; +import PageTitle from "../PageTitle/PageTitle"; +import { Suspense } from "react"; +import Button from "@mui/material/Button"; +import Stack from "@mui/material/Stack"; +import { Add } from '@mui/icons-material'; +import Link from "next/link"; +import { t } from 'i18next'; +import { Modal } from "@mui/material"; +import CustomModal from "../CustomModal/CustomModal"; +import EnterTimesheetModal from "../EnterTimesheet/EnterTimesheetModal"; + +const UserWorkspacePage: React.FC = () => { + const [isModalVisible, setModalVisible] = useState(false); + const { t } = useTranslation("home"); + + + const handleButtonClick = () => { + setModalVisible(true); + }; + + const handleCloseModal = () => { + setModalVisible(false); + }; + + return ( + + + +
+ + + + {/*fallback={}>*/} + + +
+ + +
+
+ ); +}; + +export default UserWorkspacePage; diff --git a/src/components/UserWorkspacePage/index.ts b/src/components/UserWorkspacePage/index.ts new file mode 100644 index 0000000..ec65d8f --- /dev/null +++ b/src/components/UserWorkspacePage/index.ts @@ -0,0 +1 @@ +export { default } from "./UserWorkspacePage"; diff --git a/src/theme/colorConst.js b/src/theme/colorConst.js new file mode 100644 index 0000000..2d3c0a8 --- /dev/null +++ b/src/theme/colorConst.js @@ -0,0 +1,371 @@ +import { createTheme } from "@mui/material"; +import { aborted } from "util"; + +// - - - - - - WORK IN PROGRESS - - - - - - // + +export const chartColor = [ + '#CB4047', '#ED3A41', '#F47B50', '#FBA647', + '#FDB64C', '#CCBB32', '#9ACC59', '#57B962', + '#1E83C5', '#7C4A9D' +]; + +export const chartSingleColor = [ + '#f2969a', '#fc9599', '#faa789', '#f7ae94', + '#ffd491', '#ede5a1', '#d1f5a2', '#9de0a4', + '#a2d4f5', '#b685d6' +]; + +export const rankColor = [ + '#FFD700', '#C0C0C0', '#CD853F', '#57B962', '#57B962', + '#57B962', '#57B962', '#57B962', '#57B962', '#57B962' +]; + +export const piechartColor1 = [ + '#E84A3E', '#F2883C', '#FDCD4D', '#CE478A', '#B63D2A', + '#6A8B9E', '#60667E', '#58865F', '#2F763E', '#7D80B5', +]; + +export const piechartColor2 = [ + '#6A8B9E', '#60667E', '#58865F', '#2F763E', '#7D80B5', + '#E84A3E', '#F2883C', '#FDCD4D', '#CE478A', '#B63D2A', +]; + +export const cardBorderColor = [ + '#efb142', '#4bb641', '#448df2', '#e03c04' +]; + +export const chartLineColor = [ + '#FFFFFF', '#D9D9D9' +]; + +export const GENERAL_RED_COLOR = '#e03c04'; + +export const TABLE_HEADER_TEXT_COLOR = "#3367D1"; + +export const GENERAL_INFO_COLOR = '#448df2'; + +export const GENERAL_SETTING_COLOR = '#666666'; + +export const GENERAL_BORDER_COLOR = '#e6ebf1'; + +export const GENERAL_TEXT_COLOR = '#262626'; + +export const FONT_SIZE_L = "1.875rem"; + +export const FONT_SIZE_M = "1.5rem"; + +export const FONT_SIZE_S = "1.25rem"; + +export const PROJECT_CARD_STYLE = { + borderRadius: '10px', + //border: '10px dotted #ccc', + width: '20rem', + margin: '20px', + //backgroundColor:"pink" +}; + +export const PROJECT_MODAL_STYLE = { + position: 'absolute', + width: '85%', + borderRadius: '10px', + height: '75%', + // top: '50%', + // left: '50%', + transform: 'translate(10%, 15%)', + backgroundColor: 'white', + padding: '20px', + display: 'flex', + flexDirection: 'column', + }; + +export const TAB_THEME = { + components: { + MuiTab: { + styleOverrides: { + root: { + // fontSize: '1.0rem', + fontSize: '1.25rem'//'20px', + // height: '40px', + // width: '40vw', // Default width for xs screen sizes + // '@media (min-width: 600px)': { // sm breakpoint + // width: '20vw', + // }, + // '@media (min-width: 960px)': { // md breakpoint + // width: '15vw', + // }, + // '@media (min-width: 1280px)': { // lg breakpoint + // width: '7vw', + // }, + // textTransform: "none", + // alignItems: 'center' + }, + }, + }, + } + + }; + +// copy from MTMS +export const TSMS_BUTTON_THEME = createTheme({ + palette: { + primary: { + main: '#92C1E9', + contrastText: '#FFFFFF', + }, + secondary: { + main: '#898D8D', + contrastText: '#FFFFFF', + }, + success: { + main: '#ADCAB8', + contrastText: '#FFFFFF', + }, + danger: { + main: '#F890A5', + contrastText: '#FFFFFF', + }, + warning: { + main: '#EFBE7D', + contrastText: '#FFFFFF', + }, + disable: { + main: '#B2B4B2', + contrastText: '#FFFFFF', + }, + create: { + // main: '#57B962', + main: '#ADCAB8', + // light: will be calculated from palette.primary.main, + // dark: will be calculated from palette.primary.main, + // contrastText: will be calculated to contrast with palette.primary.main + contrastText: '#FFFFFF', + }, + delete: { + // main: '#E03C04', + main: '#F890A5', + contrastText: '#FFFFFF', + + }, + cancel: { + // main: '#999999', + main: '#F890A5', + contrastText: '#FFFFFF', + }, + back: { + // main: '#999999', + main: '#898D8D', + contrastText: '#FFFFFF', + }, + reset: { + main: '#EFBE7D', + contrastText: '#FFFFFF', + }, + save: { + // main: '#448DF2', + main: '#92C1E9', + contrastText: '#FFFFFF', + }, + export: { + main: '#8C52FF', + contrastText: '#FFFFFF', + }, + import: { + main: '#92C1E9', + contrastText: '#FFFFFF', + }, + saveAs: { + main: '#FFBD59', + contrastText: '#FFFFFF', + } + }, + components: { + MuiButton: { + styleOverrides: { + root: { + '& .MuiButtonBase-root-MuiButton-root': { + fontSize: FONT_SIZE_S + }, + } + } + }, + MuiButtonBase: { + styleOverrides: { + root: { + '&.MuiChip-root.Mui-disabled': { + opacity: 0.75, + }, + '&.MuiButton-root': { + fontSize: FONT_SIZE_S + }, + } + } + }, + } +}); + +export const formTheme = createTheme({ + components: { + MuiFormLabel: { + root: { // Name of the rule + color: "rgba(0, 0, 0, 1)", + }, + styleOverrides: { + asterisk: { + color: "#db3131", + "&$error": { + color: "#db3131", + }, + }, + }, + }, + }, +}); + + +export const ARS_BUTTON_THEME = createTheme({ + palette: { + create: { + main: '#57B962', + // light: will be calculated from palette.primary.main, + // dark: will be calculated from palette.primary.main, + // contrastText: will be calculated to contrast with palette.primary.main + contrastText: '#FFFFFF', + }, + delete: { + main: '#E03C04', + contrastText: '#FFFFFF', + + }, + cancel: { + main: '#999999', + contrastText: '#FFFFFF', + + }, + save: { + main: '#448DF2', + contrastText: '#FFFFFF', + }, + export: { + main: '#8C52FF', + contrastText: '#FFFFFF', + }, + saveAs: { + main: '#FFBD59', + contrastText: '#FFFFFF', + }, + edit: { + main: '#F3AF2B', + contrastText: '#FFFFFF', + }, + exportExcel: { + main: '#6A8B9E', + contrastText: '#FFFFFF', + } + }, + components: { + MuiDataGrid: { + styleOverrides: { + actionsCell: { + '& .MuiDataGrid-actionsContainer .MuiIconButton-root': { + fontSize: '80px', // Set the desired icon size here + }, + }, + }, + }, + MuiButton: { + styleOverrides: { + root: { + // fontSize: '1.0rem', + fontSize: '1.25rem', + height: '40px', + width: '40vw', // Default width for xs screen sizes + '@media (min-width: 600px)': { // sm breakpoint + width: '20vw', + }, + '@media (min-width: 960px)': { // md breakpoint + width: '15vw', + }, + '@media (min-width: 1280px)': { // lg breakpoint + width: '7vw', + }, + textTransform: "none", + alignItems: 'center' + }, + }, + }, + } +}); + +//from ARS +export const TSMS_LONG_BUTTON_THEME = createTheme({ + palette: { + create: { + main: '#57B962', + // light: will be calculated from palette.primary.main, + // dark: will be calculated from palette.primary.main, + // contrastText: will be calculated to contrast with palette.primary.main + contrastText: '#FFFFFF', + }, + delete: { + main: '#E03C04', + contrastText: '#FFFFFF', + + }, + cancel: { + main: '#999999', + contrastText: '#FFFFFF', + + }, + save: { + main: '#448DF2', + contrastText: '#FFFFFF', + }, + export: { + main: '#8C52FF', + contrastText: '#FFFFFF', + }, + saveAs: { + main: '#FFBD59', + contrastText: '#FFFFFF', + }, + edit: { + main: '#F3AF2B', + contrastText: '#FFFFFF', + }, + exportExcel: { + main: '#60667E', + contrastText: '#FFFFFF', + } + }, + components: { + MuiDataGrid: { + styleOverrides: { + actionsCell: { + '& .MuiDataGrid-actionsContainer .MuiIconButton-root': { + fontSize: '80px', // Set the desired icon size here + }, + }, + }, + }, + MuiButton: { + styleOverrides: { + root: { + fontSize: '1.25rem', + height: '40px', + width: '40vw', // Default width for xs screen sizes + '@media (min-width: 600px)': { // sm breakpoint + width: '30vw', + }, + '@media (min-width: 960px)': { // md breakpoint + width: '25vw', + }, + '@media (min-width: 1280px)': { // lg breakpoint + width: '14vw', + }, + textTransform: "none", + alignItems: 'center' + }, + }, + }, + } +}); \ No newline at end of file