diff --git a/backend/src/db/api/tasks.js b/backend/src/db/api/tasks.js index d52d4c8..70ce62b 100644 --- a/backend/src/db/api/tasks.js +++ b/backend/src/db/api/tasks.js @@ -1,4 +1,3 @@ - const db = require('../models'); const FileDBApi = require('./file'); const crypto = require('crypto'); @@ -33,7 +32,7 @@ module.exports = class TasksDBApi { status: data.status || - null + 'Pending' , due_date: data.due_date @@ -43,26 +42,17 @@ module.exports = class TasksDBApi { priority: data.priority || - null + 'Low' , importHash: data.importHash || null, createdById: currentUser.id, updatedById: currentUser.id, + userId: data.user || currentUser.id, // Auto-assign to current user if not provided }, { transaction }, ); - - await tasks.setUser( data.user || null, { - transaction, - }); - - - - - - return tasks; } @@ -87,7 +77,7 @@ module.exports = class TasksDBApi { status: item.status || - null + 'Pending' , due_date: item.due_date @@ -97,21 +87,19 @@ module.exports = class TasksDBApi { priority: item.priority || - null + 'Low' , importHash: item.importHash || null, createdById: currentUser.id, updatedById: currentUser.id, + userId: currentUser.id, // Bulk import also assigns to importer createdAt: new Date(Date.now() + index * 1000), })); // Bulk create items const tasks = await db.tasks.bulkCreate(tasksData, { transaction }); - // For each item created, replace relation files - - return tasks; } @@ -122,47 +110,22 @@ module.exports = class TasksDBApi { const tasks = await db.tasks.findByPk(id, {}, {transaction}); - - + if (!tasks) { + throw new Error('Task not found'); + } const updatePayload = {}; if (data.title !== undefined) updatePayload.title = data.title; - - if (data.description !== undefined) updatePayload.description = data.description; - - if (data.status !== undefined) updatePayload.status = data.status; - - if (data.due_date !== undefined) updatePayload.due_date = data.due_date; - - if (data.priority !== undefined) updatePayload.priority = data.priority; - updatePayload.updatedById = currentUser.id; await tasks.update(updatePayload, {transaction}); - - - if (data.user !== undefined) { - await tasks.setUser( - - data.user, - - { transaction } - ); - } - - - - - - - return tasks; } @@ -175,6 +138,7 @@ module.exports = class TasksDBApi { id: { [Op.in]: ids, }, + userId: currentUser.id, // Only allow deleting own tasks }, transaction, }); @@ -199,7 +163,14 @@ module.exports = class TasksDBApi { const currentUser = (options && options.currentUser) || {id: null}; const transaction = (options && options.transaction) || undefined; - const tasks = await db.tasks.findByPk(id, options); + const tasks = await db.tasks.findOne({ + where: { id, userId: currentUser.id }, + transaction + }); + + if (!tasks) { + throw new Error('Task not found or permission denied'); + } await tasks.update({ deletedBy: currentUser.id @@ -228,18 +199,6 @@ module.exports = class TasksDBApi { const output = tasks.get({plain: true}); - - - - - - - output.user = await tasks.getUser({ - transaction - }); - - - return output; } @@ -252,9 +211,10 @@ module.exports = class TasksDBApi { let where = {}; const currentPage = +filter.page; - + const currentUser = (options && options.currentUser) || { id: null }; - + // RESTRICT TO OWN TASKS + where.userId = currentUser.id; offset = currentPage * limit; @@ -262,28 +222,7 @@ module.exports = class TasksDBApi { const transaction = (options && options.transaction) || undefined; - let include = [ - - { - model: db.users, - as: 'user', - - where: filter.user ? { - [Op.or]: [ - { id: { [Op.in]: filter.user.split('|').map(term => Utils.uuid(term)) } }, - { - firstName: { - [Op.or]: filter.user.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - - - ]; + let include = []; // Removed User include for privacy/simplicity in personal manager if (filter) { if (filter.id) { @@ -317,10 +256,6 @@ module.exports = class TasksDBApi { } - - - - if (filter.due_dateRange) { const [start, end] = filter.due_dateRange; @@ -369,11 +304,6 @@ module.exports = class TasksDBApi { } - - - - - if (filter.createdAtRange) { const [start, end] = filter.createdAtRange; @@ -400,8 +330,6 @@ module.exports = class TasksDBApi { } - - const queryOptions = { where, include, @@ -410,7 +338,6 @@ module.exports = class TasksDBApi { ? [[filter.field, filter.sort]] : [['createdAt', 'desc']], transaction: options?.transaction, - logging: console.log }; if (!options?.countOnly) { @@ -434,8 +361,6 @@ module.exports = class TasksDBApi { static async findAllAutocomplete(query, limit, offset, ) { let where = {}; - - if (query) { where = { [Op.or]: [ @@ -464,5 +389,4 @@ module.exports = class TasksDBApi { } -}; - +}; \ No newline at end of file diff --git a/backend/src/db/migrations/20260303000000-update-task-status-enum.js b/backend/src/db/migrations/20260303000000-update-task-status-enum.js new file mode 100644 index 0000000..f835ab6 --- /dev/null +++ b/backend/src/db/migrations/20260303000000-update-task-status-enum.js @@ -0,0 +1,26 @@ + +module.exports = { + up: async (queryInterface, Sequelize) => { + // 1. Change InProgress to In Progress in the data + await queryInterface.sequelize.query( + "UPDATE tasks SET status = 'Pending' WHERE status = 'InProgress' OR status IS NULL" + ); + + // 2. We need to handle the ENUM change. PostgreSQL ENUMs are tricky to update. + // The safest way is to rename the old enum, create a new one, and drop the old one. + // However, for this environment, we might just try to add the value if it's missing, + // or use a simpler approach if the DB allows. + + // Check current type and update it. + try { + await queryInterface.sequelize.query(`ALTER TYPE "enum_tasks_status" ADD VALUE IF NOT EXISTS 'In Progress'`); + await queryInterface.sequelize.query(`UPDATE tasks SET status = 'In Progress' WHERE status = 'InProgress'`); + } catch (e) { + console.log('Error updating enum type:', e); + } + }, + + down: async (queryInterface, Sequelize) => { + // Revert if needed + } +}; diff --git a/backend/src/db/models/tasks.js b/backend/src/db/models/tasks.js index e5cfc7d..185bbc7 100644 --- a/backend/src/db/models/tasks.js +++ b/backend/src/db/models/tasks.js @@ -16,61 +16,35 @@ module.exports = function(sequelize, DataTypes) { title: { type: DataTypes.TEXT, - - - + allowNull: false, // User requested Title to be obligatory }, description: { type: DataTypes.TEXT, - - - }, status: { type: DataTypes.ENUM, - - - values: [ - -"Pending", - - -"InProgress", - - -"Done" - + "Pending", + "In Progress", + "Done" ], - + defaultValue: "Pending", }, due_date: { type: DataTypes.DATE, - - - }, priority: { type: DataTypes.ENUM, - - - values: [ - -"Low", - - -"Medium", - - -"High" - + "Low", + "Medium", + "High" ], - + defaultValue: "Low", }, importHash: { @@ -87,20 +61,6 @@ priority: { ); tasks.associate = (db) => { - - -/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity - - - - - - - -//end loop - - - db.tasks.belongsTo(db.users, { as: 'user', foreignKey: { @@ -109,9 +69,6 @@ priority: { constraints: false, }); - - - db.tasks.belongsTo(db.users, { as: 'createdBy', }); @@ -121,9 +78,5 @@ priority: { }); }; - - return tasks; -}; - - +}; \ No newline at end of file diff --git a/frontend/src/components/Tasks/TableTasks.tsx b/frontend/src/components/Tasks/TableTasks.tsx index 5b1c2f3..b9a3d56 100644 --- a/frontend/src/components/Tasks/TableTasks.tsx +++ b/frontend/src/components/Tasks/TableTasks.tsx @@ -19,7 +19,6 @@ import {dataGridStyles} from "../../styles"; import KanbanBoard from '../KanbanBoard/KanbanBoard'; -import axios from 'axios'; const perPage = 10 @@ -30,7 +29,6 @@ const TableSampleTasks = ({ filterItems, setFilterItems, filters, showGrid }) => const dispatch = useAppDispatch(); const router = useRouter(); - const pagesList = []; const [id, setId] = useState(null); const [currentPage, setCurrentPage] = useState(0); const [filterRequest, setFilterRequest] = React.useState(''); @@ -51,10 +49,6 @@ const TableSampleTasks = ({ filterItems, setFilterItems, filters, showGrid }) => const focusRing = useAppSelector((state) => state.style.focusRingColor); const bgColor = useAppSelector((state) => state.style.bgLayoutColor); const corners = useAppSelector((state) => state.style.corners); - const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage); - for (let i = 0; i < numPages; i++) { - pagesList.push(i); - } const loadData = async (page = currentPage, request = filterRequest) => { if (page !== currentPage) setCurrentPage(page); @@ -83,35 +77,22 @@ const TableSampleTasks = ({ filterItems, setFilterItems, filters, showGrid }) => } }, [refetch, dispatch]); - const [isModalInfoActive, setIsModalInfoActive] = useState(false) const [isModalTrashActive, setIsModalTrashActive] = useState(false) const handleModalAction = () => { - setIsModalInfoActive(false) setIsModalTrashActive(false) } useEffect(() => { - - - setKanbanColumns([ - { id: "Pending", label: "Pending" }, - - { id: "InProgress", label: "InProgress" }, - + { id: "In Progress", label: "In Progress" }, { id: "Done", label: "Done" }, - ]); - - }, []); - - const handleDeleteModalAction = (id: string) => { setId(id) setIsModalTrashActive(true) @@ -159,18 +140,14 @@ const TableSampleTasks = ({ filterItems, setFilterItems, filters, showGrid }) => setFilterItems(newItems); } else { loadData(0, ''); - setKanbanFilters(''); - setFilterItems(newItems); } }; const handleSubmit = () => { loadData(0, generateFilterRequests); - setKanbanFilters(generateFilterRequests); - }; const handleChange = (id) => (e) => { @@ -190,9 +167,7 @@ const TableSampleTasks = ({ filterItems, setFilterItems, filters, showGrid }) => const handleReset = () => { setFilterItems([]); loadData(0, ''); - setKanbanFilters(''); - }; const onPageChange = (page: number) => { @@ -502,4 +477,4 @@ const TableSampleTasks = ({ filterItems, setFilterItems, filters, showGrid }) => ) } -export default TableSampleTasks +export default TableSampleTasks \ No newline at end of file diff --git a/frontend/src/components/Tasks/configureTasksCols.tsx b/frontend/src/components/Tasks/configureTasksCols.tsx index 61d7852..eafebbc 100644 --- a/frontend/src/components/Tasks/configureTasksCols.tsx +++ b/frontend/src/components/Tasks/configureTasksCols.tsx @@ -1,168 +1,106 @@ import React from 'react'; -import BaseIcon from '../BaseIcon'; -import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js'; -import axios from 'axios'; import { GridActionsCellItem, GridRowParams, - GridValueGetterParams, } from '@mui/x-data-grid'; -import ImageField from '../ImageField'; -import {saveFile} from "../../helpers/fileSaver"; -import dataFormatter from '../../helpers/dataFormatter' -import DataGridMultiSelect from "../DataGridMultiSelect"; import ListActionsPopover from '../ListActionsPopover'; - import {hasPermission} from "../../helpers/userPermissions"; +import moment from 'moment'; type Params = (id: string) => void; export const loadColumns = async ( onDelete: Params, entityName: string, - user - ) => { - async function callOptionsApi(entityName: string) { - - if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return []; - - try { - const data = await axios(`/${entityName}/autocomplete?limit=100`); - return data.data; - } catch (error) { - console.log(error); - return []; - } - } - const hasUpdatePermission = hasPermission(user, 'UPDATE_TASKS') return [ - - { - field: 'user', - headerName: 'User', - flex: 1, - minWidth: 120, - filterable: false, - headerClassName: 'datagrid--header', - cellClassName: 'datagrid--cell', - - - editable: hasUpdatePermission, - - sortable: false, - type: 'singleSelect', - getOptionValue: (value: any) => value?.id, - getOptionLabel: (value: any) => value?.label, - valueOptions: await callOptionsApi('users'), - valueGetter: (params: GridValueGetterParams) => - params?.value?.id ?? params?.value, - - }, - { field: 'title', headerName: 'Title', - flex: 1, - minWidth: 120, - filterable: false, + flex: 2, + minWidth: 200, headerClassName: 'datagrid--header', cellClassName: 'datagrid--cell', - - editable: hasUpdatePermission, - - }, - - { - field: 'description', - headerName: 'Description', - flex: 1, - minWidth: 120, - filterable: false, - headerClassName: 'datagrid--header', - cellClassName: 'datagrid--cell', - - - editable: hasUpdatePermission, - - - }, - { field: 'status', headerName: 'Status', flex: 1, minWidth: 120, - filterable: false, headerClassName: 'datagrid--header', cellClassName: 'datagrid--cell', - - editable: hasUpdatePermission, - - + renderCell: (params) => { + const colors = { + 'Pending': 'text-gray-500 bg-gray-100', + 'In Progress': 'text-blue-500 bg-blue-100', + 'Done': 'text-green-500 bg-green-100' + }; + const color = colors[params.value] || 'text-gray-500 bg-gray-100'; + return ( + + {params.value} + + ); + } }, - - { - field: 'due_date', - headerName: 'DueDate', - flex: 1, - minWidth: 120, - filterable: false, - headerClassName: 'datagrid--header', - cellClassName: 'datagrid--cell', - - - editable: hasUpdatePermission, - - type: 'dateTime', - valueGetter: (params: GridValueGetterParams) => - new Date(params.row.due_date), - - }, - { field: 'priority', headerName: 'Priority', flex: 1, - minWidth: 120, - filterable: false, + minWidth: 100, headerClassName: 'datagrid--header', cellClassName: 'datagrid--cell', - - editable: hasUpdatePermission, - - + renderCell: (params) => { + const colors = { + 'High': 'text-red-500 bg-red-100', + 'Medium': 'text-yellow-600 bg-yellow-100', + 'Low': 'text-blue-500 bg-blue-100' + }; + const color = colors[params.value] || 'text-gray-500 bg-gray-100'; + return ( + + {params.value} + + ); + } + }, + { + field: 'due_date', + headerName: 'Due Date', + flex: 1, + minWidth: 150, + headerClassName: 'datagrid--header', + cellClassName: 'datagrid--cell', + editable: hasUpdatePermission, + type: 'dateTime', + valueGetter: (params) => params.row.due_date ? new Date(params.row.due_date) : null, + renderCell: (params) => params.value ? moment(params.value).format('MMM DD, YYYY HH:mm') : '-' }, - { field: 'actions', type: 'actions', - minWidth: 30, + minWidth: 80, headerClassName: 'datagrid--header', cellClassName: 'datagrid--cell', getActions: (params: GridRowParams) => { - return [
+ onDelete={onDelete} + itemId={params?.row?.id} + pathEdit={`/tasks/tasks-edit/?id=${params?.row?.id}`} + pathView={`/tasks/tasks-view/?id=${params?.row?.id}`} + hasUpdatePermission={hasUpdatePermission} + />
, ] }, }, ]; -}; +}; \ No newline at end of file diff --git a/frontend/src/pages/tasks/tasks-edit.tsx b/frontend/src/pages/tasks/tasks-edit.tsx index ecf9517..eeec1b1 100644 --- a/frontend/src/pages/tasks/tasks-edit.tsx +++ b/frontend/src/pages/tasks/tasks-edit.tsx @@ -1,4 +1,4 @@ -import { mdiChartTimelineVariant, mdiUpload } from '@mdi/js' +import { mdiChartTimelineVariant } from '@mdi/js' import Head from 'next/head' import React, { ReactElement, useEffect, useState } from 'react' import DatePicker from "react-datepicker"; @@ -16,21 +16,10 @@ import FormField from '../../components/FormField' import BaseDivider from '../../components/BaseDivider' import BaseButtons from '../../components/BaseButtons' import BaseButton from '../../components/BaseButton' -import FormCheckRadio from '../../components/FormCheckRadio' -import FormCheckRadioGroup from '../../components/FormCheckRadioGroup' -import FormFilePicker from '../../components/FormFilePicker' -import FormImagePicker from '../../components/FormImagePicker' -import { SelectField } from "../../components/SelectField"; -import { SelectFieldMany } from "../../components/SelectFieldMany"; -import { SwitchField } from '../../components/SwitchField' -import {RichTextField} from "../../components/RichTextField"; import { update, fetch } from '../../stores/tasks/tasksSlice' import { useAppDispatch, useAppSelector } from '../../stores/hooks' import { useRouter } from 'next/router' -import {saveFile} from "../../helpers/fileSaver"; -import dataFormatter from '../../helpers/dataFormatter'; -import ImageField from "../../components/ImageField"; @@ -38,202 +27,35 @@ const EditTasksPage = () => { const router = useRouter() const dispatch = useAppDispatch() const initVals = { - - - - - - - - - - - - - - - - - - - - - - - - - user: null, - - - - - - 'title': '', - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + title: '', description: '', - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - status: '', - - - - - - - - - - - - - - - - - - - - - + status: 'Pending', due_date: new Date(), - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - priority: '', - - - - - - - - - - - + priority: 'Low', } const [initialValues, setInitialValues] = useState(initVals) const { tasks } = useAppSelector((state) => state.tasks) - - const { id } = router.query useEffect(() => { - dispatch(fetch({ id: id })) + if (id) { + dispatch(fetch({ id: id })) + } }, [id]) useEffect(() => { - if (typeof tasks === 'object') { - setInitialValues(tasks) + if (tasks && typeof tasks === 'object') { + setInitialValues({ + title: tasks.title || '', + description: tasks.description || '', + status: tasks.status || 'Pending', + due_date: tasks.due_date ? new Date(tasks.due_date) : new Date(), + priority: tasks.priority || 'Low', + }) } }, [tasks]) - useEffect(() => { - if (typeof tasks === 'object') { - const newInitialVal = {...initVals}; - Object.keys(initVals).forEach(el => newInitialVal[el] = (tasks)[el]) - setInitialValues(newInitialVal); - } - }, [tasks]) - const handleSubmit = async (data) => { await dispatch(update({ id: id, data })) await router.push('/tasks/tasks-list') @@ -242,10 +64,10 @@ const EditTasksPage = () => { return ( <> - {getPageTitle('Edit tasks')} + {getPageTitle('Edit Task')} - + {''} @@ -256,255 +78,52 @@ const EditTasksPage = () => { >
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - setInitialValues({...initialValues, 'due_date': date})} + className="w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 bg-white dark:bg-slate-800 border" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + router.push('/tasks/tasks-list')}/> @@ -519,13 +138,11 @@ const EditTasksPage = () => { EditTasksPage.getLayout = function getLayout(page: ReactElement) { return ( {page} ) } -export default EditTasksPage +export default EditTasksPage \ No newline at end of file diff --git a/frontend/src/pages/tasks/tasks-list.tsx b/frontend/src/pages/tasks/tasks-list.tsx index ac9d311..5e512e6 100644 --- a/frontend/src/pages/tasks/tasks-list.tsx +++ b/frontend/src/pages/tasks/tasks-list.tsx @@ -10,7 +10,6 @@ import { getPageTitle } from '../../config' import TableTasks from '../../components/Tasks/TableTasks' import BaseButton from '../../components/BaseButton' import axios from "axios"; -import Link from "next/link"; import {useAppDispatch, useAppSelector} from "../../stores/hooks"; import CardBoxModal from "../../components/CardBoxModal"; import DragDropFilePicker from "../../components/DragDropFilePicker"; @@ -25,7 +24,6 @@ const TasksTablesPage = () => { const [filterItems, setFilterItems] = useState([]); const [csvFile, setCsvFile] = useState(null); const [isModalActive, setIsModalActive] = useState(false); - const [showTableView, setShowTableView] = useState(false); const { currentUser } = useAppSelector((state) => state.auth); @@ -34,17 +32,12 @@ const TasksTablesPage = () => { const dispatch = useAppDispatch(); - const [filters] = useState([{label: 'Title', title: 'title'},{label: 'Description', title: 'description'}, - - - {label: 'DueDate', title: 'due_date', date: 'true'}, - - - {label: 'User', title: 'user'}, - - - - {label: 'Status', title: 'status', type: 'enum', options: ['Pending','InProgress','Done']},{label: 'Priority', title: 'priority', type: 'enum', options: ['Low','Medium','High']}, + const [filters] = useState([ + {label: 'Title', title: 'title'}, + {label: 'Description', title: 'description'}, + {label: 'DueDate', title: 'due_date', date: 'true'}, + {label: 'Status', title: 'status', type: 'enum', options: ['Pending','In Progress','Done']}, + {label: 'Priority', title: 'priority', type: 'enum', options: ['Low','Medium','High']}, ]); const hasCreatePermission = currentUser && hasPermission(currentUser, 'CREATE_TASKS'); @@ -93,12 +86,12 @@ const TasksTablesPage = () => { {getPageTitle('Tasks')} - + {''} - {hasCreatePermission && } + {hasCreatePermission && } {
-
- Switch to Table -
-
@@ -138,7 +127,6 @@ const TasksTablesPage = () => { title='Upload CSV' buttonColor='info' buttonLabel={'Confirm'} - // buttonLabel={false ? 'Deleting...' : 'Confirm'} isActive={isModalActive} onConfirm={onModalConfirm} onCancel={onModalCancel} @@ -165,4 +153,4 @@ TasksTablesPage.getLayout = function getLayout(page: ReactElement) { ) } -export default TasksTablesPage +export default TasksTablesPage \ No newline at end of file diff --git a/frontend/src/pages/tasks/tasks-new.tsx b/frontend/src/pages/tasks/tasks-new.tsx index a10f06b..644d167 100644 --- a/frontend/src/pages/tasks/tasks-new.tsx +++ b/frontend/src/pages/tasks/tasks-new.tsx @@ -1,4 +1,4 @@ -import { mdiAccount, mdiChartTimelineVariant, mdiMail, mdiUpload } from '@mdi/js' +import { mdiChartTimelineVariant } from '@mdi/js' import Head from 'next/head' import React, { ReactElement } from 'react' import CardBox from '../../components/CardBox' @@ -12,121 +12,18 @@ import FormField from '../../components/FormField' import BaseDivider from '../../components/BaseDivider' import BaseButtons from '../../components/BaseButtons' import BaseButton from '../../components/BaseButton' -import FormCheckRadio from '../../components/FormCheckRadio' -import FormCheckRadioGroup from '../../components/FormCheckRadioGroup' -import FormFilePicker from '../../components/FormFilePicker' -import FormImagePicker from '../../components/FormImagePicker' -import { SwitchField } from '../../components/SwitchField' - -import { SelectField } from '../../components/SelectField' -import { SelectFieldMany } from "../../components/SelectFieldMany"; -import {RichTextField} from "../../components/RichTextField"; import { create } from '../../stores/tasks/tasksSlice' import { useAppDispatch } from '../../stores/hooks' import { useRouter } from 'next/router' -import moment from 'moment'; const initialValues = { - - - - - - - - - - - - - user: '', - - - - title: '', - - - - - - - - - - - - - - - - description: '', - - - - - - - - - - - - - - - - - - - - - - - status: 'Pending', - - - - - - - - - - - - due_date: '', - - - - - - - - - - - - - - - - - - - priority: 'Low', - - - - - - } @@ -134,9 +31,6 @@ const TasksNew = () => { const router = useRouter() const dispatch = useAppDispatch() - - - const handleSubmit = async (data) => { await dispatch(create(data)) await router.push('/tasks/tasks-list') @@ -144,233 +38,62 @@ const TasksNew = () => { return ( <> - {getPageTitle('New Item')} + {getPageTitle('New Task')} - + {''} handleSubmit(values)} > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + router.push('/tasks/tasks-list')}/> @@ -385,13 +108,11 @@ const TasksNew = () => { TasksNew.getLayout = function getLayout(page: ReactElement) { return ( {page} ) } -export default TasksNew +export default TasksNew \ No newline at end of file