Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b0f675e27 | ||
|
|
b0ea840349 |
8
502.html
8
502.html
@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Service Starting</title>
|
||||
<title>Login</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
@ -129,8 +129,8 @@
|
||||
<p class="tip">The application is currently launching. The page will automatically refresh once site is
|
||||
available.</p>
|
||||
<div class="project-info">
|
||||
<h2>Gestor de Tareas Personales</h2>
|
||||
<p>Aplicación web para gestionar tareas personales con registro/login y CRUD con filtros por estado y prioridad.</p>
|
||||
<h2>Web page for saving and administrating time (UTTN RA2)</h2>
|
||||
<p>In this web page we're gonna see a small web page with login, Dashboard & CRUD </p>
|
||||
</div>
|
||||
<div class="loader-container">
|
||||
<img src="https://flatlogic.com/blog/wp-content/uploads/2025/05/logo-bot-1.png" alt="App Logo"
|
||||
@ -138,7 +138,7 @@
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<video width="100%" height="315" controls loop>
|
||||
<video width="20%" height="67" controls loop>
|
||||
<source
|
||||
src="https://flatlogic.com/blog/wp-content/uploads/2025/04/20250430_1336_professional_dynamo_spinner_simple_compose_01jt349yvtenxt7xhg8hhr85j8.mp4"
|
||||
type="video/mp4">
|
||||
|
||||
@ -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 {
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
@ -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
|
||||
}
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
@ -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
|
||||
@ -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 (
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-semibold ${color}`}>
|
||||
{params.value}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
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 (
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-semibold ${color}`}>
|
||||
{params.value}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
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 [
|
||||
<div key={params?.row?.id}>
|
||||
<ListActionsPopover
|
||||
onDelete={onDelete}
|
||||
itemId={params?.row?.id}
|
||||
pathEdit={`/tasks/tasks-edit/?id=${params?.row?.id}`}
|
||||
pathView={`/tasks/tasks-view/?id=${params?.row?.id}`}
|
||||
|
||||
hasUpdatePermission={hasUpdatePermission}
|
||||
|
||||
/>
|
||||
onDelete={onDelete}
|
||||
itemId={params?.row?.id}
|
||||
pathEdit={`/tasks/tasks-edit/?id=${params?.row?.id}`}
|
||||
pathView={`/tasks/tasks-view/?id=${params?.row?.id}`}
|
||||
hasUpdatePermission={hasUpdatePermission}
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
};
|
||||
@ -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 (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('Edit tasks')}</title>
|
||||
<title>{getPageTitle('Edit Task')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={'Edit tasks'} main>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={'Edit Task'} main>
|
||||
{''}
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
@ -256,255 +78,52 @@ const EditTasksPage = () => {
|
||||
>
|
||||
<Form>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label='User' labelFor='user'>
|
||||
<Field
|
||||
name='user'
|
||||
id='user'
|
||||
component={SelectField}
|
||||
options={initialValues.user}
|
||||
itemRef={'users'}
|
||||
|
||||
|
||||
showField={'firstName'}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="Title"
|
||||
required
|
||||
>
|
||||
<Field
|
||||
name="title"
|
||||
placeholder="Title"
|
||||
required
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label="Description" hasTextareaHeight>
|
||||
<Field name="description" as="textarea" placeholder="Description" />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label="Status" labelFor="status">
|
||||
<Field name="status" id="status" component="select">
|
||||
|
||||
<option value="Pending">Pending</option>
|
||||
|
||||
<option value="InProgress">InProgress</option>
|
||||
|
||||
<option value="In Progress">In Progress</option>
|
||||
<option value="Done">Done</option>
|
||||
|
||||
</Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="DueDate"
|
||||
label="Due Date"
|
||||
>
|
||||
<DatePicker
|
||||
dateFormat="yyyy-MM-dd hh:mm"
|
||||
dateFormat="yyyy-MM-dd HH:mm"
|
||||
showTimeSelect
|
||||
selected={initialValues.due_date ?
|
||||
new Date(
|
||||
dayjs(initialValues.due_date).format('YYYY-MM-DD hh:mm'),
|
||||
) : null
|
||||
}
|
||||
selected={initialValues.due_date}
|
||||
onChange={(date) => 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"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label="Priority" labelFor="priority">
|
||||
<Field name="priority" id="priority" component="select">
|
||||
|
||||
<option value="Low">Low</option>
|
||||
|
||||
<option value="Medium">Medium</option>
|
||||
|
||||
<option value="High">High</option>
|
||||
|
||||
</Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
<BaseButtons>
|
||||
<BaseButton type="submit" color="info" label="Submit" />
|
||||
<BaseButton type="submit" color="info" label="Save Changes" />
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/tasks/tasks-list')}/>
|
||||
</BaseButtons>
|
||||
@ -519,13 +138,11 @@ const EditTasksPage = () => {
|
||||
EditTasksPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'UPDATE_TASKS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default EditTasksPage
|
||||
export default EditTasksPage
|
||||
@ -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<File | null>(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 = () => {
|
||||
<title>{getPageTitle('Tasks')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="Tasks" main>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="My Tasks" main>
|
||||
{''}
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox className='mb-6' cardBoxClassName='flex flex-wrap'>
|
||||
|
||||
{hasCreatePermission && <BaseButton className={'mr-3'} href={'/tasks/tasks-new'} color='info' label='New Item'/>}
|
||||
{hasCreatePermission && <BaseButton className={'mr-3'} href={'/tasks/tasks-new'} color='info' label='New Task'/>}
|
||||
|
||||
<BaseButton
|
||||
className={'mr-3'}
|
||||
@ -120,17 +113,13 @@ const TasksTablesPage = () => {
|
||||
<div id='delete-rows-button'></div>
|
||||
</div>
|
||||
|
||||
<div className='md:inline-flex items-center ms-auto'>
|
||||
<Link href={'/tasks/tasks-table'}>Switch to Table</Link>
|
||||
</div>
|
||||
|
||||
</CardBox>
|
||||
|
||||
<TableTasks
|
||||
filterItems={filterItems}
|
||||
setFilterItems={setFilterItems}
|
||||
filters={filters}
|
||||
showGrid={false}
|
||||
showGrid={true} // Default to grid for better visibility
|
||||
/>
|
||||
|
||||
</SectionMain>
|
||||
@ -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
|
||||
@ -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 (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('New Item')}</title>
|
||||
<title>{getPageTitle('New Task')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="New Item" main>
|
||||
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="New Task" main>
|
||||
{''}
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
<Formik
|
||||
initialValues={
|
||||
|
||||
initialValues
|
||||
|
||||
}
|
||||
initialValues={initialValues}
|
||||
onSubmit={(values) => handleSubmit(values)}
|
||||
>
|
||||
<Form>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label="User" labelFor="user">
|
||||
<Field name="user" id="user" component={SelectField} options={[]} itemRef={'users'}></Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="Title"
|
||||
required
|
||||
>
|
||||
<Field
|
||||
name="title"
|
||||
placeholder="Title"
|
||||
placeholder="What needs to be done?"
|
||||
required
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label="Description" hasTextareaHeight>
|
||||
<Field name="description" as="textarea" placeholder="Description" />
|
||||
<FormField label="Description (Optional)" hasTextareaHeight>
|
||||
<Field name="description" as="textarea" placeholder="More details about this task..." />
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label="Status" labelFor="status">
|
||||
<Field name="status" id="status" component="select">
|
||||
|
||||
<option value="Pending">Pending</option>
|
||||
|
||||
<option value="InProgress">InProgress</option>
|
||||
|
||||
<option value="In Progress">In Progress</option>
|
||||
<option value="Done">Done</option>
|
||||
|
||||
</Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="DueDate"
|
||||
label="Due Date"
|
||||
>
|
||||
<Field
|
||||
type="datetime-local"
|
||||
name="due_date"
|
||||
placeholder="DueDate"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label="Priority" labelFor="priority">
|
||||
<Field name="priority" id="priority" component="select">
|
||||
|
||||
<option value="Low">Low</option>
|
||||
|
||||
<option value="Medium">Medium</option>
|
||||
|
||||
<option value="High">High</option>
|
||||
|
||||
</Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
<BaseButtons>
|
||||
<BaseButton type="submit" color="info" label="Submit" />
|
||||
<BaseButton type="submit" color="info" label="Create Task" />
|
||||
<BaseButton type="reset" color="info" outline label="Reset" />
|
||||
<BaseButton type='reset' color='danger' outline label='Cancel' onClick={() => router.push('/tasks/tasks-list')}/>
|
||||
</BaseButtons>
|
||||
@ -385,13 +108,11 @@ const TasksNew = () => {
|
||||
TasksNew.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
|
||||
permission={'CREATE_TASKS'}
|
||||
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default TasksNew
|
||||
export default TasksNew
|
||||
Loading…
x
Reference in New Issue
Block a user