diff --git a/.gitignore b/.gitignore index e427ff3..35390a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +/backend/node_modules +/frontend/node_modules node_modules/ */node_modules/ -*/build/ +**/node_modules/ +*/build/ \ No newline at end of file diff --git a/502.html b/502.html index 84e0dd6..d650847 100644 --- a/502.html +++ b/502.html @@ -129,8 +129,8 @@

The application is currently launching. The page will automatically refresh once site is available.

-

Team Projects Hub

-

Team Projects Hub for collaborative project and task management with roles, deadlines, and reporting.

+

TeamFlow Manager

+

TeamFlow Manager: collaborative project and task management with roles, deadlines, calendars, and reporting.

App Logo CREATE DATABASE db_team_projects_hub;` + - `postgres=> CREATE DATABASE db_teamflow_manager;` - Then give that new user privileges to the new database then quit the `psql`. - - `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_team_projects_hub TO admin;` + - `postgres=> GRANT ALL PRIVILEGES ON DATABASE db_teamflow_manager TO admin;` - `postgres=> \q` ------------ diff --git a/backend/package.json b/backend/package.json index 9c7733f..6b4568b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { - "name": "teamprojectshub", - "description": "Team Projects Hub - template backend", + "name": "teamflowmanager", + "description": "TeamFlow Manager - template backend", "scripts": { "start": "npm run db:migrate && npm run db:seed && npm run watch", "db:migrate": "sequelize-cli db:migrate", diff --git a/backend/src/ai/LocalAIApi.js b/backend/src/ai/LocalAIApi.js new file mode 100644 index 0000000..fd571ae --- /dev/null +++ b/backend/src/ai/LocalAIApi.js @@ -0,0 +1,484 @@ +"use strict"; + +const fs = require("fs"); +const path = require("path"); +const http = require("http"); +const https = require("https"); +const { URL } = require("url"); + +let CONFIG_CACHE = null; + +class LocalAIApi { + static createResponse(params, options) { + return createResponse(params, options); + } + + static request(pathValue, payload, options) { + return request(pathValue, payload, options); + } + + static fetchStatus(aiRequestId, options) { + return fetchStatus(aiRequestId, options); + } + + static awaitResponse(aiRequestId, options) { + return awaitResponse(aiRequestId, options); + } + + static extractText(response) { + return extractText(response); + } + + static decodeJsonFromResponse(response) { + return decodeJsonFromResponse(response); + } +} + +async function createResponse(params, options = {}) { + const payload = { ...(params || {}) }; + + if (!Array.isArray(payload.input) || payload.input.length === 0) { + return { + success: false, + error: "input_missing", + message: 'Parameter "input" is required and must be a non-empty array.', + }; + } + + const cfg = config(); + if (!payload.model) { + payload.model = cfg.defaultModel; + } + + const initial = await request(options.path, payload, options); + if (!initial.success) { + return initial; + } + + const data = initial.data; + if (data && typeof data === "object" && data.ai_request_id) { + const pollTimeout = Number(options.poll_timeout ?? 300); + const pollInterval = Number(options.poll_interval ?? 5); + return await awaitResponse(data.ai_request_id, { + interval: pollInterval, + timeout: pollTimeout, + headers: options.headers, + timeout_per_call: options.timeout, + verify_tls: options.verify_tls, + }); + } + + return initial; +} + +async function request(pathValue, payload = {}, options = {}) { + const cfg = config(); + const resolvedPath = pathValue || options.path || cfg.responsesPath; + + if (!resolvedPath) { + return { + success: false, + error: "project_id_missing", + message: "PROJECT_ID is not defined; cannot resolve AI proxy endpoint.", + }; + } + + if (!cfg.projectUuid) { + return { + success: false, + error: "project_uuid_missing", + message: "PROJECT_UUID is not defined; aborting AI request.", + }; + } + + const bodyPayload = { ...(payload || {}) }; + if (!bodyPayload.project_uuid) { + bodyPayload.project_uuid = cfg.projectUuid; + } + + const url = buildUrl(resolvedPath, cfg.baseUrl); + const timeout = resolveTimeout(options.timeout, cfg.timeout); + const verifyTls = resolveVerifyTls(options.verify_tls, cfg.verifyTls); + + const headers = { + Accept: "application/json", + "Content-Type": "application/json", + [cfg.projectHeader]: cfg.projectUuid, + }; + if (Array.isArray(options.headers)) { + for (const header of options.headers) { + if (typeof header === "string" && header.includes(":")) { + const [name, value] = header.split(":", 2); + headers[name.trim()] = value.trim(); + } + } + } + + const body = JSON.stringify(bodyPayload); + return sendRequest(url, "POST", body, headers, timeout, verifyTls); +} + +async function fetchStatus(aiRequestId, options = {}) { + const cfg = config(); + if (!cfg.projectUuid) { + return { + success: false, + error: "project_uuid_missing", + message: "PROJECT_UUID is not defined; aborting status check.", + }; + } + + const statusPath = resolveStatusPath(aiRequestId, cfg); + const url = buildUrl(statusPath, cfg.baseUrl); + const timeout = resolveTimeout(options.timeout, cfg.timeout); + const verifyTls = resolveVerifyTls(options.verify_tls, cfg.verifyTls); + + const headers = { + Accept: "application/json", + [cfg.projectHeader]: cfg.projectUuid, + }; + if (Array.isArray(options.headers)) { + for (const header of options.headers) { + if (typeof header === "string" && header.includes(":")) { + const [name, value] = header.split(":", 2); + headers[name.trim()] = value.trim(); + } + } + } + + return sendRequest(url, "GET", null, headers, timeout, verifyTls); +} + +async function awaitResponse(aiRequestId, options = {}) { + const timeout = Number(options.timeout ?? 300); + const interval = Math.max(Number(options.interval ?? 5), 1); + const deadline = Date.now() + Math.max(timeout, interval) * 1000; + + while (true) { + const statusResp = await fetchStatus(aiRequestId, { + headers: options.headers, + timeout: options.timeout_per_call, + verify_tls: options.verify_tls, + }); + + if (statusResp.success) { + const data = statusResp.data || {}; + if (data && typeof data === "object") { + if (data.status === "success") { + return { + success: true, + status: 200, + data: data.response || data, + }; + } + if (data.status === "failed") { + return { + success: false, + status: 500, + error: String(data.error || "AI request failed"), + data, + }; + } + } + } else { + return statusResp; + } + + if (Date.now() >= deadline) { + return { + success: false, + error: "timeout", + message: "Timed out waiting for AI response.", + }; + } + + await sleep(interval * 1000); + } +} + +function extractText(response) { + const payload = response && typeof response === "object" ? response.data || response : null; + if (!payload || typeof payload !== "object") { + return ""; + } + + if (Array.isArray(payload.output)) { + let combined = ""; + for (const item of payload.output) { + if (!item || !Array.isArray(item.content)) { + continue; + } + for (const block of item.content) { + if ( + block && + typeof block === "object" && + block.type === "output_text" && + typeof block.text === "string" && + block.text.length > 0 + ) { + combined += block.text; + } + } + } + if (combined) { + return combined; + } + } + + if ( + payload.choices && + payload.choices[0] && + payload.choices[0].message && + typeof payload.choices[0].message.content === "string" + ) { + return payload.choices[0].message.content; + } + + return ""; +} + +function decodeJsonFromResponse(response) { + const text = extractText(response); + if (!text) { + throw new Error("No text found in AI response."); + } + + const parsed = parseJson(text); + if (parsed.ok && parsed.value && typeof parsed.value === "object") { + return parsed.value; + } + + const stripped = stripJsonFence(text); + if (stripped !== text) { + const parsedStripped = parseJson(stripped); + if (parsedStripped.ok && parsedStripped.value && typeof parsedStripped.value === "object") { + return parsedStripped.value; + } + throw new Error(`JSON parse failed after stripping fences: ${parsedStripped.error}`); + } + + throw new Error(`JSON parse failed: ${parsed.error}`); +} + +function config() { + if (CONFIG_CACHE) { + return CONFIG_CACHE; + } + + ensureEnvLoaded(); + + const baseUrl = process.env.AI_PROXY_BASE_URL || "https://flatlogic.com"; + const projectId = process.env.PROJECT_ID || null; + let responsesPath = process.env.AI_RESPONSES_PATH || null; + if (!responsesPath && projectId) { + responsesPath = `/projects/${projectId}/ai-request`; + } + + const timeout = resolveTimeout(process.env.AI_TIMEOUT, 30); + const verifyTls = resolveVerifyTls(process.env.AI_VERIFY_TLS, true); + + CONFIG_CACHE = { + baseUrl, + responsesPath, + projectId, + projectUuid: process.env.PROJECT_UUID || null, + projectHeader: process.env.AI_PROJECT_HEADER || "project-uuid", + defaultModel: process.env.AI_DEFAULT_MODEL || "gpt-5-mini", + timeout, + verifyTls, + }; + + return CONFIG_CACHE; +} + +function buildUrl(pathValue, baseUrl) { + const trimmed = String(pathValue || "").trim(); + if (trimmed === "") { + return baseUrl; + } + if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) { + return trimmed; + } + if (trimmed.startsWith("/")) { + return `${baseUrl}${trimmed}`; + } + return `${baseUrl}/${trimmed}`; +} + +function resolveStatusPath(aiRequestId, cfg) { + const basePath = (cfg.responsesPath || "").replace(/\/+$/, ""); + if (!basePath) { + return `/ai-request/${encodeURIComponent(String(aiRequestId))}/status`; + } + const normalized = basePath.endsWith("/ai-request") ? basePath : `${basePath}/ai-request`; + return `${normalized}/${encodeURIComponent(String(aiRequestId))}/status`; +} + +function sendRequest(urlString, method, body, headers, timeoutSeconds, verifyTls) { + return new Promise((resolve) => { + let targetUrl; + try { + targetUrl = new URL(urlString); + } catch (err) { + resolve({ + success: false, + error: "invalid_url", + message: err.message, + }); + return; + } + + const isHttps = targetUrl.protocol === "https:"; + const requestFn = isHttps ? https.request : http.request; + const options = { + protocol: targetUrl.protocol, + hostname: targetUrl.hostname, + port: targetUrl.port || (isHttps ? 443 : 80), + path: `${targetUrl.pathname}${targetUrl.search}`, + method: method.toUpperCase(), + headers, + timeout: Math.max(Number(timeoutSeconds || 30), 1) * 1000, + }; + if (isHttps) { + options.rejectUnauthorized = Boolean(verifyTls); + } + + const req = requestFn(options, (res) => { + let responseBody = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + responseBody += chunk; + }); + res.on("end", () => { + const status = res.statusCode || 0; + const parsed = parseJson(responseBody); + const payload = parsed.ok ? parsed.value : responseBody; + + if (status >= 200 && status < 300) { + const result = { + success: true, + status, + data: payload, + }; + if (!parsed.ok) { + result.json_error = parsed.error; + } + resolve(result); + return; + } + + const errorMessage = + parsed.ok && payload && typeof payload === "object" + ? String(payload.error || payload.message || "AI proxy request failed") + : String(responseBody || "AI proxy request failed"); + + resolve({ + success: false, + status, + error: errorMessage, + response: payload, + json_error: parsed.ok ? undefined : parsed.error, + }); + }); + }); + + req.on("timeout", () => { + req.destroy(new Error("request_timeout")); + }); + + req.on("error", (err) => { + resolve({ + success: false, + error: "request_failed", + message: err.message, + }); + }); + + if (body) { + req.write(body); + } + req.end(); + }); +} + +function parseJson(value) { + if (typeof value !== "string" || value.trim() === "") { + return { ok: false, error: "empty_response" }; + } + try { + return { ok: true, value: JSON.parse(value) }; + } catch (err) { + return { ok: false, error: err.message }; + } +} + +function stripJsonFence(text) { + const trimmed = text.trim(); + if (trimmed.startsWith("```json")) { + return trimmed.replace(/^```json/, "").replace(/```$/, "").trim(); + } + if (trimmed.startsWith("```")) { + return trimmed.replace(/^```/, "").replace(/```$/, "").trim(); + } + return text; +} + +function resolveTimeout(value, fallback) { + const parsed = Number.parseInt(String(value ?? fallback), 10); + return Number.isNaN(parsed) ? Number(fallback) : parsed; +} + +function resolveVerifyTls(value, fallback) { + if (value === undefined || value === null) { + return Boolean(fallback); + } + return String(value).toLowerCase() !== "false" && String(value) !== "0"; +} + +function ensureEnvLoaded() { + if (process.env.PROJECT_UUID && process.env.PROJECT_ID) { + return; + } + + const envPath = path.resolve(__dirname, "../../../../.env"); + if (!fs.existsSync(envPath)) { + return; + } + + let content; + try { + content = fs.readFileSync(envPath, "utf8"); + } catch (err) { + throw new Error(`Failed to read executor .env: ${err.message}`); + } + + for (const line of content.split(/\r?\n/)) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) { + continue; + } + const [rawKey, ...rest] = trimmed.split("="); + const key = rawKey.trim(); + if (!key) { + continue; + } + const value = rest.join("=").trim().replace(/^['"]|['"]$/g, ""); + if (!process.env[key]) { + process.env[key] = value; + } + } +} + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +module.exports = { + LocalAIApi, + createResponse, + request, + fetchStatus, + awaitResponse, + extractText, + decodeJsonFromResponse, +}; diff --git a/backend/src/config.js b/backend/src/config.js index 79f9d5d..54ebf45 100644 --- a/backend/src/config.js +++ b/backend/src/config.js @@ -11,15 +11,15 @@ const config = { bcrypt: { saltRounds: 12 }, - admin_pass: "d02fa926", - user_pass: "b27b3ff8dc07", + admin_pass: "156fca4c", + user_pass: "94f5514e3457", admin_email: "admin@flatlogic.com", providers: { LOCAL: 'local', GOOGLE: 'google', MICROSOFT: 'microsoft' }, - secret_key: process.env.SECRET_KEY || 'd02fa926-6491-481e-a115-b27b3ff8dc07', + secret_key: process.env.SECRET_KEY || '156fca4c-57d0-4fe3-8a0f-94f5514e3457', remote: '', port: process.env.NODE_ENV === "production" ? "" : "8080", hostUI: process.env.NODE_ENV === "production" ? "" : "http://localhost", @@ -39,7 +39,7 @@ const config = { }, uploadDir: os.tmpdir(), email: { - from: 'Team Projects Hub ', + from: 'TeamFlow Manager ', host: 'email-smtp.us-east-1.amazonaws.com', port: 587, auth: { @@ -60,7 +60,7 @@ const config = { }, - project_uuid: 'd02fa926-6491-481e-a115-b27b3ff8dc07', + project_uuid: '156fca4c-57d0-4fe3-8a0f-94f5514e3457', flHost: process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'dev_stage' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects', diff --git a/backend/src/db/api/attachments.js b/backend/src/db/api/attachments.js deleted file mode 100644 index bd706ee..0000000 --- a/backend/src/db/api/attachments.js +++ /dev/null @@ -1,490 +0,0 @@ - -const db = require('../models'); -const FileDBApi = require('./file'); -const crypto = require('crypto'); -const Utils = require('../utils'); - - - -const Sequelize = db.Sequelize; -const Op = Sequelize.Op; - -module.exports = class AttachmentsDBApi { - - - - static async create(data, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const attachments = await db.attachments.create( - { - id: data.id || undefined, - - caption: data.caption - || - null - , - - uploaded_on: data.uploaded_on - || - null - , - - importHash: data.importHash || null, - createdById: currentUser.id, - updatedById: currentUser.id, - }, - { transaction }, - ); - - - await attachments.setTask( data.task || null, { - transaction, - }); - - await attachments.setUploaded_by( data.uploaded_by || null, { - transaction, - }); - - - - - - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.attachments.getTableName(), - belongsToColumn: 'file', - belongsToId: attachments.id, - }, - data.file, - options, - ); - - - return attachments; - } - - - static async bulkImport(data, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - // Prepare data - wrapping individual data transformations in a map() method - const attachmentsData = data.map((item, index) => ({ - id: item.id || undefined, - - caption: item.caption - || - null - , - - uploaded_on: item.uploaded_on - || - null - , - - importHash: item.importHash || null, - createdById: currentUser.id, - updatedById: currentUser.id, - createdAt: new Date(Date.now() + index * 1000), - })); - - // Bulk create items - const attachments = await db.attachments.bulkCreate(attachmentsData, { transaction }); - - // For each item created, replace relation files - - for (let i = 0; i < attachments.length; i++) { - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.attachments.getTableName(), - belongsToColumn: 'file', - belongsToId: attachments[i].id, - }, - data[i].file, - options, - ); - } - - - return attachments; - } - - static async update(id, data, options) { - const currentUser = (options && options.currentUser) || {id: null}; - const transaction = (options && options.transaction) || undefined; - - - const attachments = await db.attachments.findByPk(id, {}, {transaction}); - - - - - const updatePayload = {}; - - if (data.caption !== undefined) updatePayload.caption = data.caption; - - - if (data.uploaded_on !== undefined) updatePayload.uploaded_on = data.uploaded_on; - - - updatePayload.updatedById = currentUser.id; - - await attachments.update(updatePayload, {transaction}); - - - - if (data.task !== undefined) { - await attachments.setTask( - - data.task, - - { transaction } - ); - } - - if (data.uploaded_by !== undefined) { - await attachments.setUploaded_by( - - data.uploaded_by, - - { transaction } - ); - } - - - - - - - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.attachments.getTableName(), - belongsToColumn: 'file', - belongsToId: attachments.id, - }, - data.file, - options, - ); - - - return attachments; - } - - static async deleteByIds(ids, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const attachments = await db.attachments.findAll({ - where: { - id: { - [Op.in]: ids, - }, - }, - transaction, - }); - - await db.sequelize.transaction(async (transaction) => { - for (const record of attachments) { - await record.update( - {deletedBy: currentUser.id}, - {transaction} - ); - } - for (const record of attachments) { - await record.destroy({transaction}); - } - }); - - - return attachments; - } - - static async remove(id, options) { - const currentUser = (options && options.currentUser) || {id: null}; - const transaction = (options && options.transaction) || undefined; - - const attachments = await db.attachments.findByPk(id, options); - - await attachments.update({ - deletedBy: currentUser.id - }, { - transaction, - }); - - await attachments.destroy({ - transaction - }); - - return attachments; - } - - static async findBy(where, options) { - const transaction = (options && options.transaction) || undefined; - - const attachments = await db.attachments.findOne( - { where }, - { transaction }, - ); - - if (!attachments) { - return attachments; - } - - const output = attachments.get({plain: true}); - - - - - - - - - - - - - - - output.task = await attachments.getTask({ - transaction - }); - - - output.uploaded_by = await attachments.getUploaded_by({ - transaction - }); - - - output.file = await attachments.getFile({ - transaction - }); - - - - return output; - } - - static async findAll( - filter, - options - ) { - const limit = filter.limit || 0; - let offset = 0; - let where = {}; - const currentPage = +filter.page; - - - - - - offset = currentPage * limit; - - const orderBy = null; - - const transaction = (options && options.transaction) || undefined; - - let include = [ - - { - model: db.tasks, - as: 'task', - - where: filter.task ? { - [Op.or]: [ - { id: { [Op.in]: filter.task.split('|').map(term => Utils.uuid(term)) } }, - { - title: { - [Op.or]: filter.task.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - { - model: db.users, - as: 'uploaded_by', - - where: filter.uploaded_by ? { - [Op.or]: [ - { id: { [Op.in]: filter.uploaded_by.split('|').map(term => Utils.uuid(term)) } }, - { - firstName: { - [Op.or]: filter.uploaded_by.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - - - { - model: db.file, - as: 'file', - }, - - ]; - - if (filter) { - if (filter.id) { - where = { - ...where, - ['id']: Utils.uuid(filter.id), - }; - } - - - if (filter.caption) { - where = { - ...where, - [Op.and]: Utils.ilike( - 'attachments', - 'caption', - filter.caption, - ), - }; - } - - - - - - - if (filter.uploaded_onRange) { - const [start, end] = filter.uploaded_onRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - uploaded_on: { - ...where.uploaded_on, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - uploaded_on: { - ...where.uploaded_on, - [Op.lte]: end, - }, - }; - } - } - - - if (filter.active !== undefined) { - where = { - ...where, - active: filter.active === true || filter.active === 'true' - }; - } - - - - - - - - - - - if (filter.createdAtRange) { - const [start, end] = filter.createdAtRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - ['createdAt']: { - ...where.createdAt, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - ['createdAt']: { - ...where.createdAt, - [Op.lte]: end, - }, - }; - } - } - } - - - - - const queryOptions = { - where, - include, - distinct: true, - order: filter.field && filter.sort - ? [[filter.field, filter.sort]] - : [['createdAt', 'desc']], - transaction: options?.transaction, - logging: console.log - }; - - if (!options?.countOnly) { - queryOptions.limit = limit ? Number(limit) : undefined; - queryOptions.offset = offset ? Number(offset) : undefined; - } - - try { - const { rows, count } = await db.attachments.findAndCountAll(queryOptions); - - return { - rows: options?.countOnly ? [] : rows, - count: count - }; - } catch (error) { - console.error('Error executing query:', error); - throw error; - } - } - - static async findAllAutocomplete(query, limit, offset, ) { - let where = {}; - - - - if (query) { - where = { - [Op.or]: [ - { ['id']: Utils.uuid(query) }, - Utils.ilike( - 'attachments', - 'caption', - query, - ), - ], - }; - } - - const records = await db.attachments.findAll({ - attributes: [ 'id', 'caption' ], - where, - limit: limit ? Number(limit) : undefined, - offset: offset ? Number(offset) : undefined, - orderBy: [['caption', 'ASC']], - }); - - return records.map((record) => ({ - id: record.id, - label: record.caption, - })); - } - - -}; - diff --git a/backend/src/db/api/comments.js b/backend/src/db/api/comments.js deleted file mode 100644 index 761ea47..0000000 --- a/backend/src/db/api/comments.js +++ /dev/null @@ -1,448 +0,0 @@ - -const db = require('../models'); -const FileDBApi = require('./file'); -const crypto = require('crypto'); -const Utils = require('../utils'); - - - -const Sequelize = db.Sequelize; -const Op = Sequelize.Op; - -module.exports = class CommentsDBApi { - - - - static async create(data, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const comments = await db.comments.create( - { - id: data.id || undefined, - - content: data.content - || - null - , - - created_on: data.created_on - || - null - , - - importHash: data.importHash || null, - createdById: currentUser.id, - updatedById: currentUser.id, - }, - { transaction }, - ); - - - await comments.setTask( data.task || null, { - transaction, - }); - - await comments.setAuthor( data.author || null, { - transaction, - }); - - - - - - - return comments; - } - - - static async bulkImport(data, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - // Prepare data - wrapping individual data transformations in a map() method - const commentsData = data.map((item, index) => ({ - id: item.id || undefined, - - content: item.content - || - null - , - - created_on: item.created_on - || - null - , - - importHash: item.importHash || null, - createdById: currentUser.id, - updatedById: currentUser.id, - createdAt: new Date(Date.now() + index * 1000), - })); - - // Bulk create items - const comments = await db.comments.bulkCreate(commentsData, { transaction }); - - // For each item created, replace relation files - - - return comments; - } - - static async update(id, data, options) { - const currentUser = (options && options.currentUser) || {id: null}; - const transaction = (options && options.transaction) || undefined; - - - const comments = await db.comments.findByPk(id, {}, {transaction}); - - - - - const updatePayload = {}; - - if (data.content !== undefined) updatePayload.content = data.content; - - - if (data.created_on !== undefined) updatePayload.created_on = data.created_on; - - - updatePayload.updatedById = currentUser.id; - - await comments.update(updatePayload, {transaction}); - - - - if (data.task !== undefined) { - await comments.setTask( - - data.task, - - { transaction } - ); - } - - if (data.author !== undefined) { - await comments.setAuthor( - - data.author, - - { transaction } - ); - } - - - - - - - - return comments; - } - - static async deleteByIds(ids, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const comments = await db.comments.findAll({ - where: { - id: { - [Op.in]: ids, - }, - }, - transaction, - }); - - await db.sequelize.transaction(async (transaction) => { - for (const record of comments) { - await record.update( - {deletedBy: currentUser.id}, - {transaction} - ); - } - for (const record of comments) { - await record.destroy({transaction}); - } - }); - - - return comments; - } - - static async remove(id, options) { - const currentUser = (options && options.currentUser) || {id: null}; - const transaction = (options && options.transaction) || undefined; - - const comments = await db.comments.findByPk(id, options); - - await comments.update({ - deletedBy: currentUser.id - }, { - transaction, - }); - - await comments.destroy({ - transaction - }); - - return comments; - } - - static async findBy(where, options) { - const transaction = (options && options.transaction) || undefined; - - const comments = await db.comments.findOne( - { where }, - { transaction }, - ); - - if (!comments) { - return comments; - } - - const output = comments.get({plain: true}); - - - - - - - - - - - - - - - output.task = await comments.getTask({ - transaction - }); - - - output.author = await comments.getAuthor({ - transaction - }); - - - - return output; - } - - static async findAll( - filter, - options - ) { - const limit = filter.limit || 0; - let offset = 0; - let where = {}; - const currentPage = +filter.page; - - - - - - offset = currentPage * limit; - - const orderBy = null; - - const transaction = (options && options.transaction) || undefined; - - let include = [ - - { - model: db.tasks, - as: 'task', - - where: filter.task ? { - [Op.or]: [ - { id: { [Op.in]: filter.task.split('|').map(term => Utils.uuid(term)) } }, - { - title: { - [Op.or]: filter.task.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - { - model: db.users, - as: 'author', - - where: filter.author ? { - [Op.or]: [ - { id: { [Op.in]: filter.author.split('|').map(term => Utils.uuid(term)) } }, - { - firstName: { - [Op.or]: filter.author.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - - - ]; - - if (filter) { - if (filter.id) { - where = { - ...where, - ['id']: Utils.uuid(filter.id), - }; - } - - - if (filter.content) { - where = { - ...where, - [Op.and]: Utils.ilike( - 'comments', - 'content', - filter.content, - ), - }; - } - - - - - - - if (filter.created_onRange) { - const [start, end] = filter.created_onRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - created_on: { - ...where.created_on, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - created_on: { - ...where.created_on, - [Op.lte]: end, - }, - }; - } - } - - - if (filter.active !== undefined) { - where = { - ...where, - active: filter.active === true || filter.active === 'true' - }; - } - - - - - - - - - - - if (filter.createdAtRange) { - const [start, end] = filter.createdAtRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - ['createdAt']: { - ...where.createdAt, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - ['createdAt']: { - ...where.createdAt, - [Op.lte]: end, - }, - }; - } - } - } - - - - - const queryOptions = { - where, - include, - distinct: true, - order: filter.field && filter.sort - ? [[filter.field, filter.sort]] - : [['createdAt', 'desc']], - transaction: options?.transaction, - logging: console.log - }; - - if (!options?.countOnly) { - queryOptions.limit = limit ? Number(limit) : undefined; - queryOptions.offset = offset ? Number(offset) : undefined; - } - - try { - const { rows, count } = await db.comments.findAndCountAll(queryOptions); - - return { - rows: options?.countOnly ? [] : rows, - count: count - }; - } catch (error) { - console.error('Error executing query:', error); - throw error; - } - } - - static async findAllAutocomplete(query, limit, offset, ) { - let where = {}; - - - - if (query) { - where = { - [Op.or]: [ - { ['id']: Utils.uuid(query) }, - Utils.ilike( - 'comments', - 'content', - query, - ), - ], - }; - } - - const records = await db.comments.findAll({ - attributes: [ 'id', 'content' ], - where, - limit: limit ? Number(limit) : undefined, - offset: offset ? Number(offset) : undefined, - orderBy: [['content', 'ASC']], - }); - - return records.map((record) => ({ - id: record.id, - label: record.content, - })); - } - - -}; - diff --git a/backend/src/db/api/notifications.js b/backend/src/db/api/notifications.js deleted file mode 100644 index 5a5fe28..0000000 --- a/backend/src/db/api/notifications.js +++ /dev/null @@ -1,470 +0,0 @@ - -const db = require('../models'); -const FileDBApi = require('./file'); -const crypto = require('crypto'); -const Utils = require('../utils'); - - - -const Sequelize = db.Sequelize; -const Op = Sequelize.Op; - -module.exports = class NotificationsDBApi { - - - - static async create(data, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const notifications = await db.notifications.create( - { - id: data.id || undefined, - - message: data.message - || - null - , - - read: data.read - || - false - - , - - sent_at: data.sent_at - || - null - , - - importHash: data.importHash || null, - createdById: currentUser.id, - updatedById: currentUser.id, - }, - { transaction }, - ); - - - await notifications.setRecipient( data.recipient || null, { - transaction, - }); - - await notifications.setRelated_task( data.related_task || null, { - transaction, - }); - - - - - - - return notifications; - } - - - static async bulkImport(data, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - // Prepare data - wrapping individual data transformations in a map() method - const notificationsData = data.map((item, index) => ({ - id: item.id || undefined, - - message: item.message - || - null - , - - read: item.read - || - false - - , - - sent_at: item.sent_at - || - null - , - - importHash: item.importHash || null, - createdById: currentUser.id, - updatedById: currentUser.id, - createdAt: new Date(Date.now() + index * 1000), - })); - - // Bulk create items - const notifications = await db.notifications.bulkCreate(notificationsData, { transaction }); - - // For each item created, replace relation files - - - return notifications; - } - - static async update(id, data, options) { - const currentUser = (options && options.currentUser) || {id: null}; - const transaction = (options && options.transaction) || undefined; - - - const notifications = await db.notifications.findByPk(id, {}, {transaction}); - - - - - const updatePayload = {}; - - if (data.message !== undefined) updatePayload.message = data.message; - - - if (data.read !== undefined) updatePayload.read = data.read; - - - if (data.sent_at !== undefined) updatePayload.sent_at = data.sent_at; - - - updatePayload.updatedById = currentUser.id; - - await notifications.update(updatePayload, {transaction}); - - - - if (data.recipient !== undefined) { - await notifications.setRecipient( - - data.recipient, - - { transaction } - ); - } - - if (data.related_task !== undefined) { - await notifications.setRelated_task( - - data.related_task, - - { transaction } - ); - } - - - - - - - - return notifications; - } - - static async deleteByIds(ids, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const notifications = await db.notifications.findAll({ - where: { - id: { - [Op.in]: ids, - }, - }, - transaction, - }); - - await db.sequelize.transaction(async (transaction) => { - for (const record of notifications) { - await record.update( - {deletedBy: currentUser.id}, - {transaction} - ); - } - for (const record of notifications) { - await record.destroy({transaction}); - } - }); - - - return notifications; - } - - static async remove(id, options) { - const currentUser = (options && options.currentUser) || {id: null}; - const transaction = (options && options.transaction) || undefined; - - const notifications = await db.notifications.findByPk(id, options); - - await notifications.update({ - deletedBy: currentUser.id - }, { - transaction, - }); - - await notifications.destroy({ - transaction - }); - - return notifications; - } - - static async findBy(where, options) { - const transaction = (options && options.transaction) || undefined; - - const notifications = await db.notifications.findOne( - { where }, - { transaction }, - ); - - if (!notifications) { - return notifications; - } - - const output = notifications.get({plain: true}); - - - - - - - - - - - - - - - output.recipient = await notifications.getRecipient({ - transaction - }); - - - output.related_task = await notifications.getRelated_task({ - transaction - }); - - - - return output; - } - - static async findAll( - filter, - options - ) { - const limit = filter.limit || 0; - let offset = 0; - let where = {}; - const currentPage = +filter.page; - - - - - - offset = currentPage * limit; - - const orderBy = null; - - const transaction = (options && options.transaction) || undefined; - - let include = [ - - { - model: db.users, - as: 'recipient', - - where: filter.recipient ? { - [Op.or]: [ - { id: { [Op.in]: filter.recipient.split('|').map(term => Utils.uuid(term)) } }, - { - firstName: { - [Op.or]: filter.recipient.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - { - model: db.tasks, - as: 'related_task', - - where: filter.related_task ? { - [Op.or]: [ - { id: { [Op.in]: filter.related_task.split('|').map(term => Utils.uuid(term)) } }, - { - title: { - [Op.or]: filter.related_task.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - - - ]; - - if (filter) { - if (filter.id) { - where = { - ...where, - ['id']: Utils.uuid(filter.id), - }; - } - - - if (filter.message) { - where = { - ...where, - [Op.and]: Utils.ilike( - 'notifications', - 'message', - filter.message, - ), - }; - } - - - - - - - if (filter.sent_atRange) { - const [start, end] = filter.sent_atRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - sent_at: { - ...where.sent_at, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - sent_at: { - ...where.sent_at, - [Op.lte]: end, - }, - }; - } - } - - - if (filter.active !== undefined) { - where = { - ...where, - active: filter.active === true || filter.active === 'true' - }; - } - - - if (filter.read) { - where = { - ...where, - read: filter.read, - }; - } - - - - - - - - - - if (filter.createdAtRange) { - const [start, end] = filter.createdAtRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - ['createdAt']: { - ...where.createdAt, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - ['createdAt']: { - ...where.createdAt, - [Op.lte]: end, - }, - }; - } - } - } - - - - - const queryOptions = { - where, - include, - distinct: true, - order: filter.field && filter.sort - ? [[filter.field, filter.sort]] - : [['createdAt', 'desc']], - transaction: options?.transaction, - logging: console.log - }; - - if (!options?.countOnly) { - queryOptions.limit = limit ? Number(limit) : undefined; - queryOptions.offset = offset ? Number(offset) : undefined; - } - - try { - const { rows, count } = await db.notifications.findAndCountAll(queryOptions); - - return { - rows: options?.countOnly ? [] : rows, - count: count - }; - } catch (error) { - console.error('Error executing query:', error); - throw error; - } - } - - static async findAllAutocomplete(query, limit, offset, ) { - let where = {}; - - - - if (query) { - where = { - [Op.or]: [ - { ['id']: Utils.uuid(query) }, - Utils.ilike( - 'notifications', - 'message', - query, - ), - ], - }; - } - - const records = await db.notifications.findAll({ - attributes: [ 'id', 'message' ], - where, - limit: limit ? Number(limit) : undefined, - offset: offset ? Number(offset) : undefined, - orderBy: [['message', 'ASC']], - }); - - return records.map((record) => ({ - id: record.id, - label: record.message, - })); - } - - -}; - diff --git a/backend/src/db/api/permissions.js b/backend/src/db/api/permissions.js index 813685b..daceb98 100644 --- a/backend/src/db/api/permissions.js +++ b/backend/src/db/api/permissions.js @@ -172,10 +172,6 @@ module.exports = class PermissionsDBApi { - - - - return output; } diff --git a/backend/src/db/api/projects.js b/backend/src/db/api/projects.js index 5764cd7..bb9eaa4 100644 --- a/backend/src/db/api/projects.js +++ b/backend/src/db/api/projects.js @@ -31,6 +31,11 @@ module.exports = class ProjectsDBApi { null , + status: data.status + || + null + , + start_date: data.start_date || null @@ -41,18 +46,8 @@ module.exports = class ProjectsDBApi { null , - status: data.status - || - null - , - budget: data.budget || - null - , - - progress: data.progress - || null , @@ -76,6 +71,16 @@ module.exports = class ProjectsDBApi { + await FileDBApi.replaceRelationFiles( + { + belongsTo: db.projects.getTableName(), + belongsToColumn: 'attachments', + belongsToId: projects.id, + }, + data.attachments, + options, + ); + return projects; } @@ -97,6 +102,11 @@ module.exports = class ProjectsDBApi { description: item.description || null + , + + status: item.status + || + null , start_date: item.start_date @@ -107,21 +117,11 @@ module.exports = class ProjectsDBApi { end_date: item.end_date || null - , - - status: item.status - || - null , budget: item.budget || null - , - - progress: item.progress - || - null , importHash: item.importHash || null, @@ -135,6 +135,18 @@ module.exports = class ProjectsDBApi { // For each item created, replace relation files + for (let i = 0; i < projects.length; i++) { + await FileDBApi.replaceRelationFiles( + { + belongsTo: db.projects.getTableName(), + belongsToColumn: 'attachments', + belongsToId: projects[i].id, + }, + data[i].attachments, + options, + ); + } + return projects; } @@ -157,21 +169,18 @@ module.exports = class ProjectsDBApi { if (data.description !== undefined) updatePayload.description = data.description; + if (data.status !== undefined) updatePayload.status = data.status; + + if (data.start_date !== undefined) updatePayload.start_date = data.start_date; if (data.end_date !== undefined) updatePayload.end_date = data.end_date; - if (data.status !== undefined) updatePayload.status = data.status; - - if (data.budget !== undefined) updatePayload.budget = data.budget; - if (data.progress !== undefined) updatePayload.progress = data.progress; - - updatePayload.updatedById = currentUser.id; await projects.update(updatePayload, {transaction}); @@ -201,6 +210,16 @@ module.exports = class ProjectsDBApi { + await FileDBApi.replaceRelationFiles( + { + belongsTo: db.projects.getTableName(), + belongsToColumn: 'attachments', + belongsToId: projects.id, + }, + data.attachments, + options, + ); + return projects; } @@ -280,14 +299,6 @@ module.exports = class ProjectsDBApi { - - - output.reports_project = await projects.getReports_project({ - transaction - }); - - - output.owner = await projects.getOwner({ transaction }); @@ -298,6 +309,11 @@ module.exports = class ProjectsDBApi { }); + output.attachments = await projects.getAttachments({ + transaction + }); + + return output; } @@ -359,6 +375,11 @@ module.exports = class ProjectsDBApi { + { + model: db.file, + as: 'attachments', + }, + ]; if (filter) { @@ -487,30 +508,6 @@ module.exports = class ProjectsDBApi { } } - if (filter.progressRange) { - const [start, end] = filter.progressRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - progress: { - ...where.progress, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - progress: { - ...where.progress, - [Op.lte]: end, - }, - }; - } - } - if (filter.active !== undefined) { where = { diff --git a/backend/src/db/api/reports.js b/backend/src/db/api/reports.js deleted file mode 100644 index 487e751..0000000 --- a/backend/src/db/api/reports.js +++ /dev/null @@ -1,514 +0,0 @@ - -const db = require('../models'); -const FileDBApi = require('./file'); -const crypto = require('crypto'); -const Utils = require('../utils'); - - - -const Sequelize = db.Sequelize; -const Op = Sequelize.Op; - -module.exports = class ReportsDBApi { - - - - static async create(data, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const reports = await db.reports.create( - { - id: data.id || undefined, - - title: data.title - || - null - , - - created_on: data.created_on - || - null - , - - summary: data.summary - || - null - , - - importHash: data.importHash || null, - createdById: currentUser.id, - updatedById: currentUser.id, - }, - { transaction }, - ); - - - await reports.setProject( data.project || null, { - transaction, - }); - - await reports.setGenerated_by( data.generated_by || null, { - transaction, - }); - - - - - - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.reports.getTableName(), - belongsToColumn: 'content_file', - belongsToId: reports.id, - }, - data.content_file, - options, - ); - - - return reports; - } - - - static async bulkImport(data, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - // Prepare data - wrapping individual data transformations in a map() method - const reportsData = data.map((item, index) => ({ - id: item.id || undefined, - - title: item.title - || - null - , - - created_on: item.created_on - || - null - , - - summary: item.summary - || - null - , - - importHash: item.importHash || null, - createdById: currentUser.id, - updatedById: currentUser.id, - createdAt: new Date(Date.now() + index * 1000), - })); - - // Bulk create items - const reports = await db.reports.bulkCreate(reportsData, { transaction }); - - // For each item created, replace relation files - - for (let i = 0; i < reports.length; i++) { - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.reports.getTableName(), - belongsToColumn: 'content_file', - belongsToId: reports[i].id, - }, - data[i].content_file, - options, - ); - } - - - return reports; - } - - static async update(id, data, options) { - const currentUser = (options && options.currentUser) || {id: null}; - const transaction = (options && options.transaction) || undefined; - - - const reports = await db.reports.findByPk(id, {}, {transaction}); - - - - - const updatePayload = {}; - - if (data.title !== undefined) updatePayload.title = data.title; - - - if (data.created_on !== undefined) updatePayload.created_on = data.created_on; - - - if (data.summary !== undefined) updatePayload.summary = data.summary; - - - updatePayload.updatedById = currentUser.id; - - await reports.update(updatePayload, {transaction}); - - - - if (data.project !== undefined) { - await reports.setProject( - - data.project, - - { transaction } - ); - } - - if (data.generated_by !== undefined) { - await reports.setGenerated_by( - - data.generated_by, - - { transaction } - ); - } - - - - - - - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.reports.getTableName(), - belongsToColumn: 'content_file', - belongsToId: reports.id, - }, - data.content_file, - options, - ); - - - return reports; - } - - static async deleteByIds(ids, options) { - const currentUser = (options && options.currentUser) || { id: null }; - const transaction = (options && options.transaction) || undefined; - - const reports = await db.reports.findAll({ - where: { - id: { - [Op.in]: ids, - }, - }, - transaction, - }); - - await db.sequelize.transaction(async (transaction) => { - for (const record of reports) { - await record.update( - {deletedBy: currentUser.id}, - {transaction} - ); - } - for (const record of reports) { - await record.destroy({transaction}); - } - }); - - - return reports; - } - - static async remove(id, options) { - const currentUser = (options && options.currentUser) || {id: null}; - const transaction = (options && options.transaction) || undefined; - - const reports = await db.reports.findByPk(id, options); - - await reports.update({ - deletedBy: currentUser.id - }, { - transaction, - }); - - await reports.destroy({ - transaction - }); - - return reports; - } - - static async findBy(where, options) { - const transaction = (options && options.transaction) || undefined; - - const reports = await db.reports.findOne( - { where }, - { transaction }, - ); - - if (!reports) { - return reports; - } - - const output = reports.get({plain: true}); - - - - - - - - - - - - - - - output.project = await reports.getProject({ - transaction - }); - - - output.generated_by = await reports.getGenerated_by({ - transaction - }); - - - output.content_file = await reports.getContent_file({ - transaction - }); - - - - return output; - } - - static async findAll( - filter, - options - ) { - const limit = filter.limit || 0; - let offset = 0; - let where = {}; - const currentPage = +filter.page; - - - - - - offset = currentPage * limit; - - const orderBy = null; - - const transaction = (options && options.transaction) || undefined; - - let include = [ - - { - model: db.projects, - as: 'project', - - where: filter.project ? { - [Op.or]: [ - { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, - { - name: { - [Op.or]: filter.project.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - { - model: db.users, - as: 'generated_by', - - where: filter.generated_by ? { - [Op.or]: [ - { id: { [Op.in]: filter.generated_by.split('|').map(term => Utils.uuid(term)) } }, - { - firstName: { - [Op.or]: filter.generated_by.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - - - { - model: db.file, - as: 'content_file', - }, - - ]; - - if (filter) { - if (filter.id) { - where = { - ...where, - ['id']: Utils.uuid(filter.id), - }; - } - - - if (filter.title) { - where = { - ...where, - [Op.and]: Utils.ilike( - 'reports', - 'title', - filter.title, - ), - }; - } - - if (filter.summary) { - where = { - ...where, - [Op.and]: Utils.ilike( - 'reports', - 'summary', - filter.summary, - ), - }; - } - - - - - - - if (filter.created_onRange) { - const [start, end] = filter.created_onRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - created_on: { - ...where.created_on, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - created_on: { - ...where.created_on, - [Op.lte]: end, - }, - }; - } - } - - - if (filter.active !== undefined) { - where = { - ...where, - active: filter.active === true || filter.active === 'true' - }; - } - - - - - - - - - - - if (filter.createdAtRange) { - const [start, end] = filter.createdAtRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - ['createdAt']: { - ...where.createdAt, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - ['createdAt']: { - ...where.createdAt, - [Op.lte]: end, - }, - }; - } - } - } - - - - - const queryOptions = { - where, - include, - distinct: true, - order: filter.field && filter.sort - ? [[filter.field, filter.sort]] - : [['createdAt', 'desc']], - transaction: options?.transaction, - logging: console.log - }; - - if (!options?.countOnly) { - queryOptions.limit = limit ? Number(limit) : undefined; - queryOptions.offset = offset ? Number(offset) : undefined; - } - - try { - const { rows, count } = await db.reports.findAndCountAll(queryOptions); - - return { - rows: options?.countOnly ? [] : rows, - count: count - }; - } catch (error) { - console.error('Error executing query:', error); - throw error; - } - } - - static async findAllAutocomplete(query, limit, offset, ) { - let where = {}; - - - - if (query) { - where = { - [Op.or]: [ - { ['id']: Utils.uuid(query) }, - Utils.ilike( - 'reports', - 'title', - query, - ), - ], - }; - } - - const records = await db.reports.findAll({ - attributes: [ 'id', 'title' ], - where, - limit: limit ? Number(limit) : undefined, - offset: offset ? Number(offset) : undefined, - orderBy: [['title', 'ASC']], - }); - - return records.map((record) => ({ - id: record.id, - label: record.title, - })); - } - - -}; - diff --git a/backend/src/db/api/roles.js b/backend/src/db/api/roles.js index d20e68d..4d11d0a 100644 --- a/backend/src/db/api/roles.js +++ b/backend/src/db/api/roles.js @@ -197,10 +197,6 @@ module.exports = class RolesDBApi { - - - - output.permissions = await roles.getPermissions({ transaction }); diff --git a/backend/src/db/api/tasks.js b/backend/src/db/api/tasks.js index d1d5131..6d99211 100644 --- a/backend/src/db/api/tasks.js +++ b/backend/src/db/api/tasks.js @@ -41,21 +41,26 @@ module.exports = class TasksDBApi { null , - due_date: data.due_date - || - null - , - start_date: data.start_date || null , + due_date: data.due_date + || + null + , + estimated_hours: data.estimated_hours || null , + spent_hours: data.spent_hours + || + null + , + completed: data.completed || false @@ -70,6 +75,10 @@ module.exports = class TasksDBApi { ); + await tasks.setProject( data.project || null, { + transaction, + }); + await tasks.setAssignee( data.assignee || null, { transaction, }); @@ -78,14 +87,20 @@ module.exports = class TasksDBApi { transaction, }); - await tasks.setProject( data.project || null, { - transaction, - }); - + await FileDBApi.replaceRelationFiles( + { + belongsTo: db.tasks.getTableName(), + belongsToColumn: 'attachments', + belongsToId: tasks.id, + }, + data.attachments, + options, + ); + return tasks; } @@ -117,21 +132,26 @@ module.exports = class TasksDBApi { priority: item.priority || null - , - - due_date: item.due_date - || - null , start_date: item.start_date || null + , + + due_date: item.due_date + || + null , estimated_hours: item.estimated_hours || null + , + + spent_hours: item.spent_hours + || + null , completed: item.completed @@ -151,6 +171,18 @@ module.exports = class TasksDBApi { // For each item created, replace relation files + for (let i = 0; i < tasks.length; i++) { + await FileDBApi.replaceRelationFiles( + { + belongsTo: db.tasks.getTableName(), + belongsToColumn: 'attachments', + belongsToId: tasks[i].id, + }, + data[i].attachments, + options, + ); + } + return tasks; } @@ -179,15 +211,18 @@ module.exports = class TasksDBApi { if (data.priority !== undefined) updatePayload.priority = data.priority; - if (data.due_date !== undefined) updatePayload.due_date = data.due_date; - - if (data.start_date !== undefined) updatePayload.start_date = data.start_date; + if (data.due_date !== undefined) updatePayload.due_date = data.due_date; + + if (data.estimated_hours !== undefined) updatePayload.estimated_hours = data.estimated_hours; + if (data.spent_hours !== undefined) updatePayload.spent_hours = data.spent_hours; + + if (data.completed !== undefined) updatePayload.completed = data.completed; @@ -197,6 +232,15 @@ module.exports = class TasksDBApi { + if (data.project !== undefined) { + await tasks.setProject( + + data.project, + + { transaction } + ); + } + if (data.assignee !== undefined) { await tasks.setAssignee( @@ -215,20 +259,21 @@ module.exports = class TasksDBApi { ); } - if (data.project !== undefined) { - await tasks.setProject( - - data.project, - - { transaction } - ); - } - + await FileDBApi.replaceRelationFiles( + { + belongsTo: db.tasks.getTableName(), + belongsToColumn: 'attachments', + belongsToId: tasks.id, + }, + data.attachments, + options, + ); + return tasks; } @@ -303,23 +348,12 @@ module.exports = class TasksDBApi { - output.comments_task = await tasks.getComments_task({ + + output.project = await tasks.getProject({ transaction }); - output.attachments_task = await tasks.getAttachments_task({ - transaction - }); - - - output.notifications_related_task = await tasks.getNotifications_related_task({ - transaction - }); - - - - output.assignee = await tasks.getAssignee({ transaction }); @@ -330,7 +364,7 @@ module.exports = class TasksDBApi { }); - output.project = await tasks.getProject({ + output.attachments = await tasks.getAttachments({ transaction }); @@ -360,6 +394,23 @@ module.exports = class TasksDBApi { let include = [ + { + model: db.projects, + as: 'project', + + where: filter.project ? { + [Op.or]: [ + { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, + { + name: { + [Op.or]: filter.project.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) + } + }, + ] + } : {}, + + }, + { model: db.users, as: 'assignee', @@ -394,25 +445,13 @@ module.exports = class TasksDBApi { }, + + { - model: db.projects, - as: 'project', - - where: filter.project ? { - [Op.or]: [ - { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } }, - { - name: { - [Op.or]: filter.project.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - + model: db.file, + as: 'attachments', }, - - ]; if (filter) { @@ -451,30 +490,6 @@ module.exports = class TasksDBApi { - if (filter.due_dateRange) { - const [start, end] = filter.due_dateRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - due_date: { - ...where.due_date, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - due_date: { - ...where.due_date, - [Op.lte]: end, - }, - }; - } - } - if (filter.start_dateRange) { const [start, end] = filter.start_dateRange; @@ -499,6 +514,30 @@ module.exports = class TasksDBApi { } } + if (filter.due_dateRange) { + const [start, end] = filter.due_dateRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + due_date: { + ...where.due_date, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + due_date: { + ...where.due_date, + [Op.lte]: end, + }, + }; + } + } + if (filter.estimated_hoursRange) { const [start, end] = filter.estimated_hoursRange; @@ -523,6 +562,30 @@ module.exports = class TasksDBApi { } } + if (filter.spent_hoursRange) { + const [start, end] = filter.spent_hoursRange; + + if (start !== undefined && start !== null && start !== '') { + where = { + ...where, + spent_hours: { + ...where.spent_hours, + [Op.gte]: start, + }, + }; + } + + if (end !== undefined && end !== null && end !== '') { + where = { + ...where, + spent_hours: { + ...where.spent_hours, + [Op.lte]: end, + }, + }; + } + } + if (filter.active !== undefined) { where = { diff --git a/backend/src/db/api/team_members.js b/backend/src/db/api/team_memberships.js similarity index 72% rename from backend/src/db/api/team_members.js rename to backend/src/db/api/team_memberships.js index bd7d74e..2c6f765 100644 --- a/backend/src/db/api/team_members.js +++ b/backend/src/db/api/team_memberships.js @@ -9,7 +9,7 @@ const Utils = require('../utils'); const Sequelize = db.Sequelize; const Op = Sequelize.Op; -module.exports = class Team_membersDBApi { +module.exports = class Team_membershipsDBApi { @@ -17,22 +17,17 @@ module.exports = class Team_membersDBApi { const currentUser = (options && options.currentUser) || { id: null }; const transaction = (options && options.transaction) || undefined; - const team_members = await db.team_members.create( + const team_memberships = await db.team_memberships.create( { id: data.id || undefined, + member_label: data.member_label + || + null + , + role: data.role || - null - , - - joined_on: data.joined_on - || - null - , - - display_label: data.display_label - || null , @@ -44,11 +39,11 @@ module.exports = class Team_membersDBApi { ); - await team_members.setTeam( data.team || null, { + await team_memberships.setTeam( data.team || null, { transaction, }); - await team_members.setUser( data.user || null, { + await team_memberships.setUser( data.user || null, { transaction, }); @@ -57,7 +52,7 @@ module.exports = class Team_membersDBApi { - return team_members; + return team_memberships; } @@ -66,22 +61,17 @@ module.exports = class Team_membersDBApi { const transaction = (options && options.transaction) || undefined; // Prepare data - wrapping individual data transformations in a map() method - const team_membersData = data.map((item, index) => ({ + const team_membershipsData = data.map((item, index) => ({ id: item.id || undefined, + member_label: item.member_label + || + null + , + role: item.role || null - , - - joined_on: item.joined_on - || - null - , - - display_label: item.display_label - || - null , importHash: item.importHash || null, @@ -91,12 +81,12 @@ module.exports = class Team_membersDBApi { })); // Bulk create items - const team_members = await db.team_members.bulkCreate(team_membersData, { transaction }); + const team_memberships = await db.team_memberships.bulkCreate(team_membershipsData, { transaction }); // For each item created, replace relation files - return team_members; + return team_memberships; } static async update(id, data, options) { @@ -104,30 +94,27 @@ module.exports = class Team_membersDBApi { const transaction = (options && options.transaction) || undefined; - const team_members = await db.team_members.findByPk(id, {}, {transaction}); + const team_memberships = await db.team_memberships.findByPk(id, {}, {transaction}); const updatePayload = {}; + if (data.member_label !== undefined) updatePayload.member_label = data.member_label; + + if (data.role !== undefined) updatePayload.role = data.role; - if (data.joined_on !== undefined) updatePayload.joined_on = data.joined_on; - - - if (data.display_label !== undefined) updatePayload.display_label = data.display_label; - - updatePayload.updatedById = currentUser.id; - await team_members.update(updatePayload, {transaction}); + await team_memberships.update(updatePayload, {transaction}); if (data.team !== undefined) { - await team_members.setTeam( + await team_memberships.setTeam( data.team, @@ -136,7 +123,7 @@ module.exports = class Team_membersDBApi { } if (data.user !== undefined) { - await team_members.setUser( + await team_memberships.setUser( data.user, @@ -150,14 +137,14 @@ module.exports = class Team_membersDBApi { - return team_members; + return team_memberships; } static async deleteByIds(ids, options) { const currentUser = (options && options.currentUser) || { id: null }; const transaction = (options && options.transaction) || undefined; - const team_members = await db.team_members.findAll({ + const team_memberships = await db.team_memberships.findAll({ where: { id: { [Op.in]: ids, @@ -167,53 +154,53 @@ module.exports = class Team_membersDBApi { }); await db.sequelize.transaction(async (transaction) => { - for (const record of team_members) { + for (const record of team_memberships) { await record.update( {deletedBy: currentUser.id}, {transaction} ); } - for (const record of team_members) { + for (const record of team_memberships) { await record.destroy({transaction}); } }); - return team_members; + return team_memberships; } static async remove(id, options) { const currentUser = (options && options.currentUser) || {id: null}; const transaction = (options && options.transaction) || undefined; - const team_members = await db.team_members.findByPk(id, options); + const team_memberships = await db.team_memberships.findByPk(id, options); - await team_members.update({ + await team_memberships.update({ deletedBy: currentUser.id }, { transaction, }); - await team_members.destroy({ + await team_memberships.destroy({ transaction }); - return team_members; + return team_memberships; } static async findBy(where, options) { const transaction = (options && options.transaction) || undefined; - const team_members = await db.team_members.findOne( + const team_memberships = await db.team_memberships.findOne( { where }, { transaction }, ); - if (!team_members) { - return team_members; + if (!team_memberships) { + return team_memberships; } - const output = team_members.get({plain: true}); + const output = team_memberships.get({plain: true}); @@ -224,16 +211,12 @@ module.exports = class Team_membersDBApi { - - - - - output.team = await team_members.getTeam({ + output.team = await team_memberships.getTeam({ transaction }); - output.user = await team_members.getUser({ + output.user = await team_memberships.getUser({ transaction }); @@ -310,13 +293,13 @@ module.exports = class Team_membersDBApi { } - if (filter.display_label) { + if (filter.member_label) { where = { ...where, [Op.and]: Utils.ilike( - 'team_members', - 'display_label', - filter.display_label, + 'team_memberships', + 'member_label', + filter.member_label, ), }; } @@ -326,30 +309,6 @@ module.exports = class Team_membersDBApi { - if (filter.joined_onRange) { - const [start, end] = filter.joined_onRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - joined_on: { - ...where.joined_on, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - joined_on: { - ...where.joined_on, - [Op.lte]: end, - }, - }; - } - } - if (filter.active !== undefined) { where = { @@ -419,7 +378,7 @@ module.exports = class Team_membersDBApi { } try { - const { rows, count } = await db.team_members.findAndCountAll(queryOptions); + const { rows, count } = await db.team_memberships.findAndCountAll(queryOptions); return { rows: options?.countOnly ? [] : rows, @@ -441,25 +400,25 @@ module.exports = class Team_membersDBApi { [Op.or]: [ { ['id']: Utils.uuid(query) }, Utils.ilike( - 'team_members', - 'display_label', + 'team_memberships', + 'member_label', query, ), ], }; } - const records = await db.team_members.findAll({ - attributes: [ 'id', 'display_label' ], + const records = await db.team_memberships.findAll({ + attributes: [ 'id', 'member_label' ], where, limit: limit ? Number(limit) : undefined, offset: offset ? Number(offset) : undefined, - orderBy: [['display_label', 'ASC']], + orderBy: [['member_label', 'ASC']], }); return records.map((record) => ({ id: record.id, - label: record.display_label, + label: record.member_label, })); } diff --git a/backend/src/db/api/teams.js b/backend/src/db/api/teams.js index 995cfb9..6306911 100644 --- a/backend/src/db/api/teams.js +++ b/backend/src/db/api/teams.js @@ -28,16 +28,6 @@ module.exports = class TeamsDBApi { description: data.description || - null - , - - visibility: data.visibility - || - null - , - - members_count: data.members_count - || null , @@ -49,24 +39,10 @@ module.exports = class TeamsDBApi { ); - await teams.setOwner( data.owner || null, { - transaction, - }); - - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.teams.getTableName(), - belongsToColumn: 'avatar', - belongsToId: teams.id, - }, - data.avatar, - options, - ); - return teams; } @@ -88,16 +64,6 @@ module.exports = class TeamsDBApi { description: item.description || null - , - - visibility: item.visibility - || - null - , - - members_count: item.members_count - || - null , importHash: item.importHash || null, @@ -111,18 +77,6 @@ module.exports = class TeamsDBApi { // For each item created, replace relation files - for (let i = 0; i < teams.length; i++) { - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.teams.getTableName(), - belongsToColumn: 'avatar', - belongsToId: teams[i].id, - }, - data[i].avatar, - options, - ); - } - return teams; } @@ -145,42 +99,17 @@ module.exports = class TeamsDBApi { if (data.description !== undefined) updatePayload.description = data.description; - if (data.visibility !== undefined) updatePayload.visibility = data.visibility; - - - if (data.members_count !== undefined) updatePayload.members_count = data.members_count; - - updatePayload.updatedById = currentUser.id; await teams.update(updatePayload, {transaction}); - if (data.owner !== undefined) { - await teams.setOwner( - - data.owner, - - { transaction } - ); - } - - await FileDBApi.replaceRelationFiles( - { - belongsTo: db.teams.getTableName(), - belongsToColumn: 'avatar', - belongsToId: teams.id, - }, - data.avatar, - options, - ); - return teams; } @@ -252,7 +181,7 @@ module.exports = class TeamsDBApi { - output.team_members_team = await teams.getTeam_members_team({ + output.team_memberships_team = await teams.getTeam_memberships_team({ transaction }); @@ -264,20 +193,6 @@ module.exports = class TeamsDBApi { - - - - - output.avatar = await teams.getAvatar({ - transaction - }); - - - output.owner = await teams.getOwner({ - transaction - }); - - return output; } @@ -303,30 +218,8 @@ module.exports = class TeamsDBApi { let include = [ - { - model: db.users, - as: 'owner', - - where: filter.owner ? { - [Op.or]: [ - { id: { [Op.in]: filter.owner.split('|').map(term => Utils.uuid(term)) } }, - { - firstName: { - [Op.or]: filter.owner.split('|').map(term => ({ [Op.iLike]: `%${term}%` })) - } - }, - ] - } : {}, - - }, - - { - model: db.file, - as: 'avatar', - }, - ]; if (filter) { @@ -365,30 +258,6 @@ module.exports = class TeamsDBApi { - if (filter.members_countRange) { - const [start, end] = filter.members_countRange; - - if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - members_count: { - ...where.members_count, - [Op.gte]: start, - }, - }; - } - - if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - members_count: { - ...where.members_count, - [Op.lte]: end, - }, - }; - } - } - if (filter.active !== undefined) { where = { @@ -398,17 +267,8 @@ module.exports = class TeamsDBApi { } - if (filter.visibility) { - where = { - ...where, - visibility: filter.visibility, - }; - } - - - if (filter.createdAtRange) { diff --git a/backend/src/db/api/users.js b/backend/src/db/api/users.js index d3ee52e..4bed437 100644 --- a/backend/src/db/api/users.js +++ b/backend/src/db/api/users.js @@ -403,12 +403,8 @@ module.exports = class UsersDBApi { - output.teams_owner = await users.getTeams_owner({ - transaction - }); - - output.team_members_user = await users.getTeam_members_user({ + output.team_memberships_user = await users.getTeam_memberships_user({ transaction }); @@ -427,26 +423,6 @@ module.exports = class UsersDBApi { }); - output.comments_author = await users.getComments_author({ - transaction - }); - - - output.attachments_uploaded_by = await users.getAttachments_uploaded_by({ - transaction - }); - - - output.notifications_recipient = await users.getNotifications_recipient({ - transaction - }); - - - output.reports_generated_by = await users.getReports_generated_by({ - transaction - }); - - output.avatar = await users.getAvatar({ transaction diff --git a/backend/src/db/db.config.js b/backend/src/db/db.config.js index 4c88153..7c7dc83 100644 --- a/backend/src/db/db.config.js +++ b/backend/src/db/db.config.js @@ -15,7 +15,7 @@ module.exports = { username: 'postgres', dialect: 'postgres', password: '', - database: 'db_team_projects_hub', + database: 'db_teamflow_manager', host: process.env.DB_HOST || 'localhost', logging: console.log, seederStorage: 'sequelize', diff --git a/backend/src/db/migrations/1767609012299.js b/backend/src/db/migrations/1767710423367.js similarity index 63% rename from backend/src/db/migrations/1767609012299.js rename to backend/src/db/migrations/1767710423367.js index 0e0f4f0..a0d0508 100644 --- a/backend/src/db/migrations/1767609012299.js +++ b/backend/src/db/migrations/1767710423367.js @@ -140,7 +140,7 @@ module.exports = { - await queryInterface.createTable('team_members', { + await queryInterface.createTable('team_memberships', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, @@ -236,134 +236,6 @@ module.exports = { - await queryInterface.createTable('comments', { - id: { - type: Sequelize.DataTypes.UUID, - defaultValue: Sequelize.DataTypes.UUIDV4, - primaryKey: true, - }, - createdById: { - type: Sequelize.DataTypes.UUID, - references: { - key: 'id', - model: 'users', - }, - }, - updatedById: { - type: Sequelize.DataTypes.UUID, - references: { - key: 'id', - model: 'users', - }, - }, - createdAt: { type: Sequelize.DataTypes.DATE }, - updatedAt: { type: Sequelize.DataTypes.DATE }, - deletedAt: { type: Sequelize.DataTypes.DATE }, - importHash: { - type: Sequelize.DataTypes.STRING(255), - allowNull: true, - unique: true, - }, - }, { transaction }); - - - - await queryInterface.createTable('attachments', { - id: { - type: Sequelize.DataTypes.UUID, - defaultValue: Sequelize.DataTypes.UUIDV4, - primaryKey: true, - }, - createdById: { - type: Sequelize.DataTypes.UUID, - references: { - key: 'id', - model: 'users', - }, - }, - updatedById: { - type: Sequelize.DataTypes.UUID, - references: { - key: 'id', - model: 'users', - }, - }, - createdAt: { type: Sequelize.DataTypes.DATE }, - updatedAt: { type: Sequelize.DataTypes.DATE }, - deletedAt: { type: Sequelize.DataTypes.DATE }, - importHash: { - type: Sequelize.DataTypes.STRING(255), - allowNull: true, - unique: true, - }, - }, { transaction }); - - - - await queryInterface.createTable('notifications', { - id: { - type: Sequelize.DataTypes.UUID, - defaultValue: Sequelize.DataTypes.UUIDV4, - primaryKey: true, - }, - createdById: { - type: Sequelize.DataTypes.UUID, - references: { - key: 'id', - model: 'users', - }, - }, - updatedById: { - type: Sequelize.DataTypes.UUID, - references: { - key: 'id', - model: 'users', - }, - }, - createdAt: { type: Sequelize.DataTypes.DATE }, - updatedAt: { type: Sequelize.DataTypes.DATE }, - deletedAt: { type: Sequelize.DataTypes.DATE }, - importHash: { - type: Sequelize.DataTypes.STRING(255), - allowNull: true, - unique: true, - }, - }, { transaction }); - - - - await queryInterface.createTable('reports', { - id: { - type: Sequelize.DataTypes.UUID, - defaultValue: Sequelize.DataTypes.UUIDV4, - primaryKey: true, - }, - createdById: { - type: Sequelize.DataTypes.UUID, - references: { - key: 'id', - model: 'users', - }, - }, - updatedById: { - type: Sequelize.DataTypes.UUID, - references: { - key: 'id', - model: 'users', - }, - }, - createdAt: { type: Sequelize.DataTypes.DATE }, - updatedAt: { type: Sequelize.DataTypes.DATE }, - deletedAt: { type: Sequelize.DataTypes.DATE }, - importHash: { - type: Sequelize.DataTypes.STRING(255), - allowNull: true, - unique: true, - }, - }, { transaction }); - - - await queryInterface.addColumn( 'users', @@ -653,29 +525,10 @@ module.exports = { await queryInterface.addColumn( - 'teams', - 'visibility', + 'team_memberships', + 'member_label', { - type: Sequelize.DataTypes.ENUM, - - - values: ['Private','Public'], - - - }, - { transaction } - ); - - - - - - - await queryInterface.addColumn( - 'teams', - 'members_count', - { - type: Sequelize.DataTypes.INTEGER, + type: Sequelize.DataTypes.TEXT, @@ -687,27 +540,7 @@ module.exports = { await queryInterface.addColumn( - 'teams', - 'ownerId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'users', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'team_members', + 'team_memberships', 'teamId', { type: Sequelize.DataTypes.UUID, @@ -727,7 +560,7 @@ module.exports = { await queryInterface.addColumn( - 'team_members', + 'team_memberships', 'userId', { type: Sequelize.DataTypes.UUID, @@ -747,7 +580,7 @@ module.exports = { await queryInterface.addColumn( - 'team_members', + 'team_memberships', 'role', { type: Sequelize.DataTypes.ENUM, @@ -763,36 +596,6 @@ module.exports = { - await queryInterface.addColumn( - 'team_members', - 'joined_on', - { - type: Sequelize.DataTypes.DATE, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'team_members', - 'display_label', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - await queryInterface.addColumn( 'projects', 'name', @@ -823,53 +626,6 @@ module.exports = { - await queryInterface.addColumn( - 'projects', - 'start_date', - { - type: Sequelize.DataTypes.DATE, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'projects', - 'end_date', - { - type: Sequelize.DataTypes.DATE, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'projects', - 'status', - { - type: Sequelize.DataTypes.ENUM, - - - values: ['Planned','Active','OnHold','Completed','Archived'], - - - }, - { transaction } - ); - - - - await queryInterface.addColumn( 'projects', 'ownerId', @@ -912,9 +668,26 @@ module.exports = { await queryInterface.addColumn( 'projects', - 'budget', + 'status', { - type: Sequelize.DataTypes.DECIMAL, + type: Sequelize.DataTypes.ENUM, + + + values: ['Draft','Active','OnHold','Completed','Cancelled'], + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'projects', + 'start_date', + { + type: Sequelize.DataTypes.DATE, @@ -927,9 +700,9 @@ module.exports = { await queryInterface.addColumn( 'projects', - 'progress', + 'end_date', { - type: Sequelize.DataTypes.INTEGER, + type: Sequelize.DataTypes.DATE, @@ -940,6 +713,23 @@ module.exports = { + await queryInterface.addColumn( + 'projects', + 'budget', + { + type: Sequelize.DataTypes.DECIMAL, + + + + }, + { transaction } + ); + + + + + + await queryInterface.addColumn( 'tasks', 'title', @@ -972,30 +762,16 @@ module.exports = { await queryInterface.addColumn( 'tasks', - 'status', + 'projectId', { - type: Sequelize.DataTypes.ENUM, + type: Sequelize.DataTypes.UUID, - values: ['Backlog','ToDo','InProgress','InReview','Done','Blocked'], - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'tasks', - 'priority', - { - type: Sequelize.DataTypes.ENUM, - - - values: ['Low','Medium','High','Critical'], + references: { + model: 'projects', + key: 'id', + }, }, { transaction } @@ -1046,16 +822,13 @@ module.exports = { await queryInterface.addColumn( 'tasks', - 'projectId', + 'status', { - type: Sequelize.DataTypes.UUID, + type: Sequelize.DataTypes.ENUM, + values: ['ToDo','InProgress','InReview','Blocked','Done'], - references: { - model: 'projects', - key: 'id', - }, }, { transaction } @@ -1066,7 +839,24 @@ module.exports = { await queryInterface.addColumn( 'tasks', - 'due_date', + 'priority', + { + type: Sequelize.DataTypes.ENUM, + + + values: ['Low','Medium','High','Critical'], + + + }, + { transaction } + ); + + + + + await queryInterface.addColumn( + 'tasks', + 'start_date', { type: Sequelize.DataTypes.DATE, @@ -1081,7 +871,7 @@ module.exports = { await queryInterface.addColumn( 'tasks', - 'start_date', + 'due_date', { type: Sequelize.DataTypes.DATE, @@ -1109,6 +899,23 @@ module.exports = { + await queryInterface.addColumn( + 'tasks', + 'spent_hours', + { + type: Sequelize.DataTypes.DECIMAL, + + + + }, + { transaction } + ); + + + + + + await queryInterface.addColumn( 'tasks', 'completed', @@ -1125,323 +932,6 @@ module.exports = { ); - - - await queryInterface.addColumn( - 'comments', - 'taskId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'tasks', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'comments', - 'authorId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'users', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'comments', - 'content', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'comments', - 'created_on', - { - type: Sequelize.DataTypes.DATE, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'attachments', - 'taskId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'tasks', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'attachments', - 'uploaded_byId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'users', - key: 'id', - }, - - }, - { transaction } - ); - - - - - - - await queryInterface.addColumn( - 'attachments', - 'caption', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'attachments', - 'uploaded_on', - { - type: Sequelize.DataTypes.DATE, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'notifications', - 'recipientId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'users', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'notifications', - 'message', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'notifications', - 'related_taskId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'tasks', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'notifications', - 'read', - { - type: Sequelize.DataTypes.BOOLEAN, - - defaultValue: false, - allowNull: false, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'notifications', - 'sent_at', - { - type: Sequelize.DataTypes.DATE, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'reports', - 'title', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'reports', - 'projectId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'projects', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'reports', - 'generated_byId', - { - type: Sequelize.DataTypes.UUID, - - - - references: { - model: 'users', - key: 'id', - }, - - }, - { transaction } - ); - - - - - await queryInterface.addColumn( - 'reports', - 'created_on', - { - type: Sequelize.DataTypes.DATE, - - - - }, - { transaction } - ); - - - - - - - await queryInterface.addColumn( - 'reports', - 'summary', - { - type: Sequelize.DataTypes.TEXT, - - - - }, - { transaction } - ); - - await transaction.commit(); @@ -1464,8 +954,8 @@ module.exports = { await queryInterface.removeColumn( - 'reports', - 'summary', + 'tasks', + 'completed', { transaction } ); @@ -1473,147 +963,9 @@ module.exports = { - await queryInterface.removeColumn( - 'reports', - 'created_on', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'reports', - 'generated_byId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'reports', - 'projectId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'reports', - 'title', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'notifications', - 'sent_at', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'notifications', - 'read', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'notifications', - 'related_taskId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'notifications', - 'message', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'notifications', - 'recipientId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'attachments', - 'uploaded_on', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'attachments', - 'caption', - { transaction } - ); - - - - - - await queryInterface.removeColumn( - 'attachments', - 'uploaded_byId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'attachments', - 'taskId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'comments', - 'created_on', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'comments', - 'content', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'comments', - 'authorId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'comments', - 'taskId', - { transaction } - ); - - - await queryInterface.removeColumn( 'tasks', - 'completed', + 'spent_hours', { transaction } ); @@ -1627,14 +979,6 @@ module.exports = { - await queryInterface.removeColumn( - 'tasks', - 'start_date', - { transaction } - ); - - - await queryInterface.removeColumn( 'tasks', 'due_date', @@ -1645,23 +989,7 @@ module.exports = { await queryInterface.removeColumn( 'tasks', - 'projectId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'tasks', - 'reporterId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'tasks', - 'assigneeId', + 'start_date', { transaction } ); @@ -1683,6 +1011,30 @@ module.exports = { + await queryInterface.removeColumn( + 'tasks', + 'reporterId', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'tasks', + 'assigneeId', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'tasks', + 'projectId', + { transaction } + ); + + + await queryInterface.removeColumn( 'tasks', 'description', @@ -1699,12 +1051,6 @@ module.exports = { - await queryInterface.removeColumn( - 'projects', - 'progress', - { transaction } - ); - await queryInterface.removeColumn( @@ -1715,30 +1061,6 @@ module.exports = { - await queryInterface.removeColumn( - 'projects', - 'teamId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'projects', - 'ownerId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'projects', - 'status', - { transaction } - ); - - - await queryInterface.removeColumn( 'projects', 'end_date', @@ -1755,6 +1077,30 @@ module.exports = { + await queryInterface.removeColumn( + 'projects', + 'status', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'projects', + 'teamId', + { transaction } + ); + + + + await queryInterface.removeColumn( + 'projects', + 'ownerId', + { transaction } + ); + + + await queryInterface.removeColumn( 'projects', 'description', @@ -1772,23 +1118,7 @@ module.exports = { await queryInterface.removeColumn( - 'team_members', - 'display_label', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'team_members', - 'joined_on', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'team_members', + 'team_memberships', 'role', { transaction } ); @@ -1796,7 +1126,7 @@ module.exports = { await queryInterface.removeColumn( - 'team_members', + 'team_memberships', 'userId', { transaction } ); @@ -1804,7 +1134,7 @@ module.exports = { await queryInterface.removeColumn( - 'team_members', + 'team_memberships', 'teamId', { transaction } ); @@ -1812,26 +1142,8 @@ module.exports = { await queryInterface.removeColumn( - 'teams', - 'ownerId', - { transaction } - ); - - - - await queryInterface.removeColumn( - 'teams', - 'members_count', - { transaction } - ); - - - - - - await queryInterface.removeColumn( - 'teams', - 'visibility', + 'team_memberships', + 'member_label', { transaction } ); @@ -1987,22 +1299,6 @@ module.exports = { - await queryInterface.dropTable('reports', { transaction }); - - - - await queryInterface.dropTable('notifications', { transaction }); - - - - await queryInterface.dropTable('attachments', { transaction }); - - - - await queryInterface.dropTable('comments', { transaction }); - - - await queryInterface.dropTable('tasks', { transaction }); @@ -2011,7 +1307,7 @@ module.exports = { - await queryInterface.dropTable('team_members', { transaction }); + await queryInterface.dropTable('team_memberships', { transaction }); diff --git a/backend/src/db/models/attachments.js b/backend/src/db/models/attachments.js deleted file mode 100644 index 2558d8e..0000000 --- a/backend/src/db/models/attachments.js +++ /dev/null @@ -1,109 +0,0 @@ -const config = require('../../config'); -const providers = config.providers; -const crypto = require('crypto'); -const bcrypt = require('bcrypt'); -const moment = require('moment'); - -module.exports = function(sequelize, DataTypes) { - const attachments = sequelize.define( - 'attachments', - { - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true, - }, - -caption: { - type: DataTypes.TEXT, - - - - }, - -uploaded_on: { - type: DataTypes.DATE, - - - - }, - - importHash: { - type: DataTypes.STRING(255), - allowNull: true, - unique: true, - }, - }, - { - timestamps: true, - paranoid: true, - freezeTableName: true, - }, - ); - - attachments.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.attachments.belongsTo(db.tasks, { - as: 'task', - foreignKey: { - name: 'taskId', - }, - constraints: false, - }); - - db.attachments.belongsTo(db.users, { - as: 'uploaded_by', - foreignKey: { - name: 'uploaded_byId', - }, - constraints: false, - }); - - - - db.attachments.hasMany(db.file, { - as: 'file', - foreignKey: 'belongsToId', - constraints: false, - scope: { - belongsTo: db.attachments.getTableName(), - belongsToColumn: 'file', - }, - }); - - - db.attachments.belongsTo(db.users, { - as: 'createdBy', - }); - - db.attachments.belongsTo(db.users, { - as: 'updatedBy', - }); - }; - - - - return attachments; -}; - - diff --git a/backend/src/db/models/comments.js b/backend/src/db/models/comments.js deleted file mode 100644 index f76258d..0000000 --- a/backend/src/db/models/comments.js +++ /dev/null @@ -1,99 +0,0 @@ -const config = require('../../config'); -const providers = config.providers; -const crypto = require('crypto'); -const bcrypt = require('bcrypt'); -const moment = require('moment'); - -module.exports = function(sequelize, DataTypes) { - const comments = sequelize.define( - 'comments', - { - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true, - }, - -content: { - type: DataTypes.TEXT, - - - - }, - -created_on: { - type: DataTypes.DATE, - - - - }, - - importHash: { - type: DataTypes.STRING(255), - allowNull: true, - unique: true, - }, - }, - { - timestamps: true, - paranoid: true, - freezeTableName: true, - }, - ); - - comments.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.comments.belongsTo(db.tasks, { - as: 'task', - foreignKey: { - name: 'taskId', - }, - constraints: false, - }); - - db.comments.belongsTo(db.users, { - as: 'author', - foreignKey: { - name: 'authorId', - }, - constraints: false, - }); - - - - - db.comments.belongsTo(db.users, { - as: 'createdBy', - }); - - db.comments.belongsTo(db.users, { - as: 'updatedBy', - }); - }; - - - - return comments; -}; - - diff --git a/backend/src/db/models/notifications.js b/backend/src/db/models/notifications.js deleted file mode 100644 index 09a9be9..0000000 --- a/backend/src/db/models/notifications.js +++ /dev/null @@ -1,109 +0,0 @@ -const config = require('../../config'); -const providers = config.providers; -const crypto = require('crypto'); -const bcrypt = require('bcrypt'); -const moment = require('moment'); - -module.exports = function(sequelize, DataTypes) { - const notifications = sequelize.define( - 'notifications', - { - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true, - }, - -message: { - type: DataTypes.TEXT, - - - - }, - -read: { - type: DataTypes.BOOLEAN, - - allowNull: false, - defaultValue: false, - - - - }, - -sent_at: { - type: DataTypes.DATE, - - - - }, - - importHash: { - type: DataTypes.STRING(255), - allowNull: true, - unique: true, - }, - }, - { - timestamps: true, - paranoid: true, - freezeTableName: true, - }, - ); - - notifications.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.notifications.belongsTo(db.users, { - as: 'recipient', - foreignKey: { - name: 'recipientId', - }, - constraints: false, - }); - - db.notifications.belongsTo(db.tasks, { - as: 'related_task', - foreignKey: { - name: 'related_taskId', - }, - constraints: false, - }); - - - - - db.notifications.belongsTo(db.users, { - as: 'createdBy', - }); - - db.notifications.belongsTo(db.users, { - as: 'updatedBy', - }); - }; - - - - return notifications; -}; - - diff --git a/backend/src/db/models/permissions.js b/backend/src/db/models/permissions.js index 53431d8..c6a1206 100644 --- a/backend/src/db/models/permissions.js +++ b/backend/src/db/models/permissions.js @@ -48,10 +48,6 @@ name: { - - - - //end loop diff --git a/backend/src/db/models/projects.js b/backend/src/db/models/projects.js index 58f5c57..14c265c 100644 --- a/backend/src/db/models/projects.js +++ b/backend/src/db/models/projects.js @@ -28,6 +28,31 @@ description: { }, +status: { + type: DataTypes.ENUM, + + + + values: [ + +"Draft", + + +"Active", + + +"OnHold", + + +"Completed", + + +"Cancelled" + + ], + + }, + start_date: { type: DataTypes.DATE, @@ -42,43 +67,11 @@ end_date: { }, -status: { - type: DataTypes.ENUM, - - - - values: [ - -"Planned", - - -"Active", - - -"OnHold", - - -"Completed", - - -"Archived" - - ], - - }, - budget: { type: DataTypes.DECIMAL, - }, - -progress: { - type: DataTypes.INTEGER, - - - }, importHash: { @@ -116,18 +109,6 @@ progress: { - - - db.projects.hasMany(db.reports, { - as: 'reports_project', - foreignKey: { - name: 'projectId', - }, - constraints: false, - }); - - - //end loop @@ -150,6 +131,16 @@ progress: { + db.projects.hasMany(db.file, { + as: 'attachments', + foreignKey: 'belongsToId', + constraints: false, + scope: { + belongsTo: db.projects.getTableName(), + belongsToColumn: 'attachments', + }, + }); + db.projects.belongsTo(db.users, { as: 'createdBy', diff --git a/backend/src/db/models/reports.js b/backend/src/db/models/reports.js deleted file mode 100644 index ada3c95..0000000 --- a/backend/src/db/models/reports.js +++ /dev/null @@ -1,116 +0,0 @@ -const config = require('../../config'); -const providers = config.providers; -const crypto = require('crypto'); -const bcrypt = require('bcrypt'); -const moment = require('moment'); - -module.exports = function(sequelize, DataTypes) { - const reports = sequelize.define( - 'reports', - { - id: { - type: DataTypes.UUID, - defaultValue: DataTypes.UUIDV4, - primaryKey: true, - }, - -title: { - type: DataTypes.TEXT, - - - - }, - -created_on: { - type: DataTypes.DATE, - - - - }, - -summary: { - type: DataTypes.TEXT, - - - - }, - - importHash: { - type: DataTypes.STRING(255), - allowNull: true, - unique: true, - }, - }, - { - timestamps: true, - paranoid: true, - freezeTableName: true, - }, - ); - - reports.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.reports.belongsTo(db.projects, { - as: 'project', - foreignKey: { - name: 'projectId', - }, - constraints: false, - }); - - db.reports.belongsTo(db.users, { - as: 'generated_by', - foreignKey: { - name: 'generated_byId', - }, - constraints: false, - }); - - - - db.reports.hasMany(db.file, { - as: 'content_file', - foreignKey: 'belongsToId', - constraints: false, - scope: { - belongsTo: db.reports.getTableName(), - belongsToColumn: 'content_file', - }, - }); - - - db.reports.belongsTo(db.users, { - as: 'createdBy', - }); - - db.reports.belongsTo(db.users, { - as: 'updatedBy', - }); - }; - - - - return reports; -}; - - diff --git a/backend/src/db/models/roles.js b/backend/src/db/models/roles.js index 696cb39..e26c222 100644 --- a/backend/src/db/models/roles.js +++ b/backend/src/db/models/roles.js @@ -81,10 +81,6 @@ role_customization: { - - - - //end loop diff --git a/backend/src/db/models/tasks.js b/backend/src/db/models/tasks.js index e33e1b1..737a6e7 100644 --- a/backend/src/db/models/tasks.js +++ b/backend/src/db/models/tasks.js @@ -35,9 +35,6 @@ status: { values: [ -"Backlog", - - "ToDo", @@ -47,10 +44,10 @@ status: { "InReview", -"Done", +"Blocked", -"Blocked" +"Done" ], @@ -78,18 +75,18 @@ priority: { }, -due_date: { - type: DataTypes.DATE, - - - - }, - start_date: { type: DataTypes.DATE, + }, + +due_date: { + type: DataTypes.DATE, + + + }, estimated_hours: { @@ -97,6 +94,13 @@ estimated_hours: { + }, + +spent_hours: { + type: DataTypes.DECIMAL, + + + }, completed: { @@ -135,39 +139,19 @@ completed: { - db.tasks.hasMany(db.comments, { - as: 'comments_task', - foreignKey: { - name: 'taskId', - }, - constraints: false, - }); - - - db.tasks.hasMany(db.attachments, { - as: 'attachments_task', - foreignKey: { - name: 'taskId', - }, - constraints: false, - }); - - - db.tasks.hasMany(db.notifications, { - as: 'notifications_related_task', - foreignKey: { - name: 'related_taskId', - }, - constraints: false, - }); - - - //end loop + db.tasks.belongsTo(db.projects, { + as: 'project', + foreignKey: { + name: 'projectId', + }, + constraints: false, + }); + db.tasks.belongsTo(db.users, { as: 'assignee', foreignKey: { @@ -184,17 +168,19 @@ completed: { constraints: false, }); - db.tasks.belongsTo(db.projects, { - as: 'project', - foreignKey: { - name: 'projectId', - }, + + + db.tasks.hasMany(db.file, { + as: 'attachments', + foreignKey: 'belongsToId', constraints: false, + scope: { + belongsTo: db.tasks.getTableName(), + belongsToColumn: 'attachments', + }, }); - - db.tasks.belongsTo(db.users, { as: 'createdBy', }); diff --git a/backend/src/db/models/team_members.js b/backend/src/db/models/team_memberships.js similarity index 75% rename from backend/src/db/models/team_members.js rename to backend/src/db/models/team_memberships.js index f8d9536..d3f6ed5 100644 --- a/backend/src/db/models/team_members.js +++ b/backend/src/db/models/team_memberships.js @@ -5,8 +5,8 @@ const bcrypt = require('bcrypt'); const moment = require('moment'); module.exports = function(sequelize, DataTypes) { - const team_members = sequelize.define( - 'team_members', + const team_memberships = sequelize.define( + 'team_memberships', { id: { type: DataTypes.UUID, @@ -14,6 +14,13 @@ module.exports = function(sequelize, DataTypes) { primaryKey: true, }, +member_label: { + type: DataTypes.TEXT, + + + + }, + role: { type: DataTypes.ENUM, @@ -30,20 +37,6 @@ role: { }, -joined_on: { - type: DataTypes.DATE, - - - - }, - -display_label: { - type: DataTypes.TEXT, - - - - }, - importHash: { type: DataTypes.STRING(255), allowNull: true, @@ -57,7 +50,7 @@ display_label: { }, ); - team_members.associate = (db) => { + team_memberships.associate = (db) => { /// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity @@ -71,15 +64,11 @@ display_label: { - - - - //end loop - db.team_members.belongsTo(db.teams, { + db.team_memberships.belongsTo(db.teams, { as: 'team', foreignKey: { name: 'teamId', @@ -87,7 +76,7 @@ display_label: { constraints: false, }); - db.team_members.belongsTo(db.users, { + db.team_memberships.belongsTo(db.users, { as: 'user', foreignKey: { name: 'userId', @@ -98,18 +87,18 @@ display_label: { - db.team_members.belongsTo(db.users, { + db.team_memberships.belongsTo(db.users, { as: 'createdBy', }); - db.team_members.belongsTo(db.users, { + db.team_memberships.belongsTo(db.users, { as: 'updatedBy', }); }; - return team_members; + return team_memberships; }; diff --git a/backend/src/db/models/teams.js b/backend/src/db/models/teams.js index d78b0f3..8158ba5 100644 --- a/backend/src/db/models/teams.js +++ b/backend/src/db/models/teams.js @@ -26,29 +26,6 @@ description: { - }, - -visibility: { - type: DataTypes.ENUM, - - - - values: [ - -"Private", - - -"Public" - - ], - - }, - -members_count: { - type: DataTypes.INTEGER, - - - }, importHash: { @@ -74,8 +51,8 @@ members_count: { - db.teams.hasMany(db.team_members, { - as: 'team_members_team', + db.teams.hasMany(db.team_memberships, { + as: 'team_memberships_team', foreignKey: { name: 'teamId', }, @@ -94,35 +71,13 @@ members_count: { - - - - //end loop - db.teams.belongsTo(db.users, { - as: 'owner', - foreignKey: { - name: 'ownerId', - }, - constraints: false, - }); - db.teams.hasMany(db.file, { - as: 'avatar', - foreignKey: 'belongsToId', - constraints: false, - scope: { - belongsTo: db.teams.getTableName(), - belongsToColumn: 'avatar', - }, - }); - - db.teams.belongsTo(db.users, { as: 'createdBy', }); diff --git a/backend/src/db/models/users.js b/backend/src/db/models/users.js index 2ba107d..9a8f480 100644 --- a/backend/src/db/models/users.js +++ b/backend/src/db/models/users.js @@ -144,17 +144,9 @@ provider: { - db.users.hasMany(db.teams, { - as: 'teams_owner', - foreignKey: { - name: 'ownerId', - }, - constraints: false, - }); - - db.users.hasMany(db.team_members, { - as: 'team_members_user', + db.users.hasMany(db.team_memberships, { + as: 'team_memberships_user', foreignKey: { name: 'userId', }, @@ -188,42 +180,6 @@ provider: { }); - db.users.hasMany(db.comments, { - as: 'comments_author', - foreignKey: { - name: 'authorId', - }, - constraints: false, - }); - - - db.users.hasMany(db.attachments, { - as: 'attachments_uploaded_by', - foreignKey: { - name: 'uploaded_byId', - }, - constraints: false, - }); - - - db.users.hasMany(db.notifications, { - as: 'notifications_recipient', - foreignKey: { - name: 'recipientId', - }, - constraints: false, - }); - - - db.users.hasMany(db.reports, { - as: 'reports_generated_by', - foreignKey: { - name: 'generated_byId', - }, - constraints: false, - }); - - //end loop diff --git a/backend/src/db/seeders/20200430130760-user-roles.js b/backend/src/db/seeders/20200430130760-user-roles.js index 3e0a439..fae87b8 100644 --- a/backend/src/db/seeders/20200430130760-user-roles.js +++ b/backend/src/db/seeders/20200430130760-user-roles.js @@ -33,11 +33,11 @@ module.exports = { - { id: getId("ProjectOwner"), name: "Project Owner", createdAt, updatedAt }, + { id: getId("PlatformLead"), name: "Platform Lead", createdAt, updatedAt }, - { id: getId("ProjectManager"), name: "Project Manager", createdAt, updatedAt }, + { id: getId("ProjectLead"), name: "Project Lead", createdAt, updatedAt }, - { id: getId("TeamLead"), name: "Team Lead", createdAt, updatedAt }, + { id: getId("TeamManager"), name: "Team Manager", createdAt, updatedAt }, { id: getId("Contributor"), name: "Contributor", createdAt, updatedAt }, @@ -61,7 +61,7 @@ module.exports = { } const entities = [ - "users","roles","permissions","teams","team_members","projects","tasks","comments","attachments","notifications","reports",, + "users","roles","permissions","teams","team_memberships","projects","tasks",, ]; await queryInterface.bulkInsert("permissions", entities.flatMap(createPermissions)); await queryInterface.bulkInsert("permissions", [{ id: getId(`READ_API_DOCS`), createdAt, updatedAt, name: `READ_API_DOCS` }]); @@ -90,19 +90,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('CREATE_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('CREATE_USERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('READ_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('READ_USERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('UPDATE_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('UPDATE_USERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('DELETE_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('DELETE_USERS') }, @@ -113,12 +113,16 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('READ_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('READ_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('UPDATE_USERS') }, + + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('DELETE_USERS') }, + @@ -128,10 +132,12 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('READ_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('READ_USERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('UPDATE_USERS') }, + @@ -183,40 +189,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('CREATE_TEAMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('CREATE_TEAMS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('READ_TEAMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('READ_TEAMS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('UPDATE_TEAMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('UPDATE_TEAMS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('DELETE_TEAMS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('CREATE_TEAMS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('READ_TEAMS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('UPDATE_TEAMS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('DELETE_TEAMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('DELETE_TEAMS') }, @@ -227,11 +212,34 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('READ_TEAMS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('READ_TEAMS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('UPDATE_TEAMS') }, + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('CREATE_TEAMS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('READ_TEAMS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('UPDATE_TEAMS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('DELETE_TEAMS') }, @@ -280,61 +288,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('CREATE_TEAM_MEMBERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('CREATE_TEAM_MEMBERSHIPS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('READ_TEAM_MEMBERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('READ_TEAM_MEMBERSHIPS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('UPDATE_TEAM_MEMBERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('UPDATE_TEAM_MEMBERSHIPS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('DELETE_TEAM_MEMBERS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('CREATE_TEAM_MEMBERS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('READ_TEAM_MEMBERS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('UPDATE_TEAM_MEMBERS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('DELETE_TEAM_MEMBERS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('CREATE_TEAM_MEMBERS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('READ_TEAM_MEMBERS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('UPDATE_TEAM_MEMBERS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('DELETE_TEAM_MEMBERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('DELETE_TEAM_MEMBERSHIPS') }, @@ -345,7 +311,45 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('READ_TEAM_MEMBERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('READ_TEAM_MEMBERSHIPS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('UPDATE_TEAM_MEMBERSHIPS') }, + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('CREATE_TEAM_MEMBERSHIPS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('READ_TEAM_MEMBERSHIPS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('UPDATE_TEAM_MEMBERSHIPS') }, + + + + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('DELETE_TEAM_MEMBERSHIPS') }, + + + + + + + + + + + { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('READ_TEAM_MEMBERSHIPS') }, @@ -360,7 +364,7 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("Viewer"), permissionId: getId('READ_TEAM_MEMBERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Viewer"), permissionId: getId('READ_TEAM_MEMBERSHIPS') }, @@ -383,19 +387,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('CREATE_PROJECTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('CREATE_PROJECTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('READ_PROJECTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('READ_PROJECTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('UPDATE_PROJECTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('UPDATE_PROJECTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('DELETE_PROJECTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('DELETE_PROJECTS') }, @@ -404,19 +408,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('CREATE_PROJECTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('CREATE_PROJECTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('READ_PROJECTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('READ_PROJECTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('UPDATE_PROJECTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('UPDATE_PROJECTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('DELETE_PROJECTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('DELETE_PROJECTS') }, @@ -425,15 +429,13 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('CREATE_PROJECTS') }, + + + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('READ_PROJECTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('READ_PROJECTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('UPDATE_PROJECTS') }, + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('UPDATE_PROJECTS') }, @@ -484,19 +486,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('CREATE_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('CREATE_TASKS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('READ_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('READ_TASKS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('UPDATE_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('UPDATE_TASKS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('DELETE_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('DELETE_TASKS') }, @@ -505,19 +507,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('CREATE_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('CREATE_TASKS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('READ_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('READ_TASKS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('UPDATE_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('UPDATE_TASKS') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('DELETE_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('DELETE_TASKS') }, @@ -526,19 +528,19 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('CREATE_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('CREATE_TASKS') }, - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('READ_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('READ_TASKS') }, - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('UPDATE_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('UPDATE_TASKS') }, - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('DELETE_TASKS') }, + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('DELETE_TASKS') }, @@ -581,436 +583,16 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('CREATE_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('READ_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('UPDATE_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('DELETE_COMMENTS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('CREATE_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('READ_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('UPDATE_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('DELETE_COMMENTS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('CREATE_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('READ_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('UPDATE_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('DELETE_COMMENTS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('CREATE_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('READ_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('UPDATE_COMMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('DELETE_COMMENTS') }, - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("Viewer"), permissionId: getId('READ_COMMENTS') }, - - - - - - - - - - - - - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('CREATE_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('READ_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('UPDATE_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('DELETE_ATTACHMENTS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('CREATE_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('READ_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('UPDATE_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('DELETE_ATTACHMENTS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('CREATE_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('READ_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('UPDATE_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('DELETE_ATTACHMENTS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('CREATE_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('READ_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('UPDATE_ATTACHMENTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('DELETE_ATTACHMENTS') }, - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("Viewer"), permissionId: getId('READ_ATTACHMENTS') }, - - - - - - - - - - - - - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('CREATE_NOTIFICATIONS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('READ_NOTIFICATIONS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('UPDATE_NOTIFICATIONS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('DELETE_NOTIFICATIONS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('CREATE_NOTIFICATIONS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('READ_NOTIFICATIONS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('UPDATE_NOTIFICATIONS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('DELETE_NOTIFICATIONS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('CREATE_NOTIFICATIONS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('READ_NOTIFICATIONS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('UPDATE_NOTIFICATIONS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('DELETE_NOTIFICATIONS') }, - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('READ_NOTIFICATIONS') }, - - - - - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("Viewer"), permissionId: getId('READ_NOTIFICATIONS') }, - - - - - - - - - - - - - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('CREATE_REPORTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('READ_REPORTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('UPDATE_REPORTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('DELETE_REPORTS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('CREATE_REPORTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('READ_REPORTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('UPDATE_REPORTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('DELETE_REPORTS') }, - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('CREATE_REPORTS') }, - - - - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('READ_REPORTS') }, - - - - - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('READ_REPORTS') }, - - - - - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("Viewer"), permissionId: getId('READ_REPORTS') }, - - - - - - - - - - - - - { createdAt, updatedAt, roles_permissionsId: getId("ProjectOwner"), permissionId: getId('CREATE_SEARCH') }, + { createdAt, updatedAt, roles_permissionsId: getId("PlatformLead"), permissionId: getId('CREATE_SEARCH') }, - { createdAt, updatedAt, roles_permissionsId: getId("ProjectManager"), permissionId: getId('CREATE_SEARCH') }, + { createdAt, updatedAt, roles_permissionsId: getId("ProjectLead"), permissionId: getId('CREATE_SEARCH') }, - { createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), permissionId: getId('CREATE_SEARCH') }, + { createdAt, updatedAt, roles_permissionsId: getId("TeamManager"), permissionId: getId('CREATE_SEARCH') }, { createdAt, updatedAt, roles_permissionsId: getId("Contributor"), permissionId: getId('CREATE_SEARCH') }, @@ -1039,10 +621,10 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_TEAMS') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_TEAMS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_TEAM_MEMBERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_TEAM_MEMBERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_TEAM_MEMBERS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_TEAM_MEMBERS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_TEAM_MEMBERSHIPS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_TEAM_MEMBERSHIPS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_TEAM_MEMBERSHIPS') }, + { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_TEAM_MEMBERSHIPS') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_PROJECTS') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_PROJECTS') }, @@ -1054,26 +636,6 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_TASKS') }, { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_TASKS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_COMMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_COMMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_COMMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_COMMENTS') }, - - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_ATTACHMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_ATTACHMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_ATTACHMENTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_ATTACHMENTS') }, - - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_NOTIFICATIONS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_NOTIFICATIONS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_NOTIFICATIONS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_NOTIFICATIONS') }, - - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('CREATE_REPORTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_REPORTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_REPORTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_REPORTS') }, - { createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_API_DOCS') }, @@ -1089,8 +651,8 @@ await queryInterface.bulkInsert("rolesPermissionsPermissions", [ - await queryInterface.sequelize.query(`UPDATE "users" SET "app_roleId"='${getId("ProjectOwner")}' WHERE "email"='client@hello.com'`); - await queryInterface.sequelize.query(`UPDATE "users" SET "app_roleId"='${getId("ProjectManager")}' WHERE "email"='john@doe.com'`); + await queryInterface.sequelize.query(`UPDATE "users" SET "app_roleId"='${getId("PlatformLead")}' WHERE "email"='client@hello.com'`); + await queryInterface.sequelize.query(`UPDATE "users" SET "app_roleId"='${getId("ProjectLead")}' WHERE "email"='john@doe.com'`); diff --git a/backend/src/db/seeders/20231127130745-sample-data.js b/backend/src/db/seeders/20231127130745-sample-data.js index 5ad3028..69fb926 100644 --- a/backend/src/db/seeders/20231127130745-sample-data.js +++ b/backend/src/db/seeders/20231127130745-sample-data.js @@ -30,20 +30,12 @@ const Users = db.users; const Teams = db.teams; -const TeamMembers = db.team_members; +const TeamMemberships = db.team_memberships; const Projects = db.projects; const Tasks = db.tasks; -const Comments = db.comments; - -const Attachments = db.attachments; - -const Notifications = db.notifications; - -const Reports = db.reports; - @@ -57,42 +49,14 @@ const TeamsData = [ - "name": "Core Team", + "name": "Core Platform", - "description": "Cross functional core team handling product strategy and delivery", - - - - - - - "visibility": "Private", - - - - - - - // type code here for "images" field - - - - - - - "members_count": 5, - - - - - - - // type code here for "relation_one" field + "description": "Platform services and shared infrastructure", @@ -110,35 +74,7 @@ const TeamsData = [ - "description": "Mobile engineers building native apps", - - - - - - - "visibility": "Public", - - - - - - - // type code here for "images" field - - - - - - - "members_count": 3, - - - - - - - // type code here for "relation_one" field + "description": "Native mobile applications and releases", @@ -149,42 +85,50 @@ const TeamsData = [ - "name": "Design Team", + "name": "Design Studio", - "description": "Designers focused on UX and UI", + "description": "Product design, UX research, and visual system", + + + + }, + + { + + + + + "name": "Data Science", - "visibility": "Public", + "description": "Analytics, ML models, and reporting", + + + + }, + + { + + + + + "name": "Customer Success", - // type code here for "images" field - - - - - - - "members_count": 2, - - - - - - - // type code here for "relation_one" field + "description": "Onboarding, support, and customer feedback", @@ -194,13 +138,20 @@ const TeamsData = [ -const TeamMembersData = [ +const TeamMembershipsData = [ { + "member_label": "Alice - Platform Lead", + + + + + + // type code here for "relation_one" field @@ -219,20 +170,6 @@ const TeamMembersData = [ - - - - "joined_on": new Date('2025-06-01T09:00:00Z'), - - - - - - - "display_label": "Alice Johnson - Core Team", - - - }, { @@ -240,6 +177,13 @@ const TeamMembersData = [ + "member_label": "Ben - Frontend", + + + + + + // type code here for "relation_one" field @@ -258,17 +202,35 @@ const TeamMembersData = [ + }, + + { + - "joined_on": new Date('2025-06-15T10:30:00Z'), + "member_label": "Carmen - Design Lead", - "display_label": "Bob Smith - Core Team", + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field + + + + + + + "role": "Member", @@ -279,6 +241,13 @@ const TeamMembersData = [ + "member_label": "Daniel - Data Engineer", + + + + + + // type code here for "relation_one" field @@ -297,17 +266,35 @@ const TeamMembersData = [ + }, + + { + - "joined_on": new Date('2025-05-20T12:00:00Z'), + "member_label": "Eve - Customer Success", - "display_label": "Carol Nguyen - Design Team", + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field + + + + + + + "role": "Admin", @@ -331,21 +318,155 @@ const ProjectsData = [ - "description": "Modernize the public website with updated branding and improved performance", + "description": "Refresh marketing site and improve conversion funnels", - "start_date": new Date('2025-09-01T09:00:00Z'), + // type code here for "relation_one" field - "end_date": new Date('2026-02-28T17:00:00Z'), + // type code here for "relation_one" field + + + + + + + "status": "OnHold", + + + + + + + "start_date": new Date('2025-01-06T09:00:00Z'), + + + + + + + "end_date": new Date('2025-03-15T17:00:00Z'), + + + + + + + "budget": 45000, + + + + + + + // type code here for "files" field + + + + }, + + { + + + + + "name": "Mobile App Launch", + + + + + + + "description": "Launch iOS and Android native apps for public release", + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field + + + + + + + "status": "Cancelled", + + + + + + + "start_date": new Date('2025-02-01T09:00:00Z'), + + + + + + + "end_date": new Date('2025-06-30T17:00:00Z'), + + + + + + + "budget": 120000, + + + + + + + // type code here for "files" field + + + + }, + + { + + + + + "name": "Analytics Pipeline", + + + + + + + "description": "Implement ETL and reporting for key metrics", + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field @@ -359,28 +480,28 @@ const ProjectsData = [ - // type code here for "relation_one" field + "start_date": new Date('2025-01-20T10:00:00Z'), - // type code here for "relation_one" field + "end_date": new Date('2025-04-01T17:00:00Z'), - "budget": 25000.0, + "budget": 60000, - "progress": 45, + // type code here for "files" field @@ -391,35 +512,14 @@ const ProjectsData = [ - "name": "Mobile App V2", + "name": "Support Portal", - "description": "Second major version of the mobile app with offline support", - - - - - - - "start_date": new Date('2025-08-15T09:00:00Z'), - - - - - - - "end_date": new Date('2026-04-30T17:00:00Z'), - - - - - - - "status": "Planned", + "description": "Customer self service portal and knowledge base", @@ -440,14 +540,35 @@ const ProjectsData = [ - "budget": 40000.0, + "status": "OnHold", - "progress": 10, + "start_date": new Date('2024-11-15T09:00:00Z'), + + + + + + + "end_date": new Date('2025-05-01T17:00:00Z'), + + + + + + + "budget": 25000, + + + + + + + // type code here for "files" field @@ -458,35 +579,14 @@ const ProjectsData = [ - "name": "Design System", + "name": "Marketing Campaign Q2", - "description": "Create a shared design system and component library", - - - - - - - "start_date": new Date('2025-07-01T09:00:00Z'), - - - - - - - "end_date": new Date('2025-12-31T17:00:00Z'), - - - - - - - "status": "Completed", + "description": "Quarterly campaign to boost user acquisition", @@ -507,14 +607,35 @@ const ProjectsData = [ - "budget": 15000.0, + "status": "Draft", - "progress": 60, + "start_date": new Date('2025-04-01T09:00:00Z'), + + + + + + + "end_date": new Date('2025-06-30T17:00:00Z'), + + + + + + + "budget": 30000, + + + + + + + // type code here for "files" field @@ -531,14 +652,35 @@ const TasksData = [ - "title": "Landing Page Mockups", + "title": "Create homepage wireframes", - "description": "Create high fidelity mockups for the new landing page", + "description": "Produce initial wireframes for the new homepage", + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field @@ -552,49 +694,42 @@ const TasksData = [ - "priority": "High", + "priority": "Critical", - // type code here for "relation_one" field + "start_date": new Date('2025-01-07T09:00:00Z'), - // type code here for "relation_one" field + "due_date": new Date('2025-01-20T17:00:00Z'), - // type code here for "relation_one" field + "estimated_hours": 40, - "due_date": new Date('2026-01-15T17:00:00Z'), + "spent_hours": 12, - "start_date": new Date('2025-12-01T09:00:00Z'), - - - - - - - "estimated_hours": 40.0, + // type code here for "files" field @@ -612,28 +747,14 @@ const TasksData = [ - "title": "Implement Auth API", + "title": "Implement auth endpoints", - "description": "Develop authentication endpoints and token management", - - - - - - - "status": "ToDo", - - - - - - - "priority": "High", + "description": "Add token based authentication and refresh flow", @@ -661,21 +782,49 @@ const TasksData = [ - "due_date": new Date('2026-02-01T17:00:00Z'), + "status": "InProgress", - "start_date": new Date('2025-11-20T09:00:00Z'), + "priority": "Low", - "estimated_hours": 80.0, + "start_date": new Date('2025-02-10T09:00:00Z'), + + + + + + + "due_date": new Date('2025-02-24T17:00:00Z'), + + + + + + + "estimated_hours": 32, + + + + + + + "spent_hours": 0, + + + + + + + // type code here for "files" field @@ -693,14 +842,35 @@ const TasksData = [ - "title": "Mobile Offline Sync", + "title": "Schedule ETL backfill", - "description": "Design and implement offline data synchronization for mobile", + "description": "Plan and run backfill for historical metrics", + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field + + + + + + + // type code here for "relation_one" field @@ -714,49 +884,42 @@ const TasksData = [ - "priority": "High", + "priority": "Medium", - // type code here for "relation_one" field + "start_date": new Date('2025-01-25T09:00:00Z'), - // type code here for "relation_one" field + "due_date": new Date('2025-02-10T17:00:00Z'), - // type code here for "relation_one" field + "estimated_hours": 24, - "due_date": new Date('2026-03-15T17:00:00Z'), + "spent_hours": 6, - "start_date": new Date('2026-01-10T09:00:00Z'), - - - - - - - "estimated_hours": 120.0, + // type code here for "files" field @@ -769,49 +932,25 @@ const TasksData = [ }, -]; - - - -const CommentsData = [ - { - // type code here for "relation_one" field + "title": "Draft support knowledge base", - // type code here for "relation_one" field + "description": "Create initial articles for top customer issues", - "content": "Initial mockups uploaded for review", - - - - - - - "created_on": new Date('2025-12-05T10:20:00Z'), - - - - }, - - { - - - - // type code here for "relation_one" field @@ -826,24 +965,6 @@ const CommentsData = [ - "content": "Auth endpoints draft PR is ready for review", - - - - - - - "created_on": new Date('2025-11-25T15:40:00Z'), - - - - }, - - { - - - - // type code here for "relation_one" field @@ -851,45 +972,42 @@ const CommentsData = [ - // type code here for "relation_one" field + "status": "Blocked", - "content": "Component library includes button and form primitives", + "priority": "Critical", - "created_on": new Date('2025-10-12T09:30:00Z'), - - - - }, - -]; - - - -const AttachmentsData = [ - - { - - - - - // type code here for "relation_one" field + "start_date": new Date('2024-12-01T09:00:00Z'), - // type code here for "relation_one" field + "due_date": new Date('2025-01-15T17:00:00Z'), + + + + + + + "estimated_hours": 16, + + + + + + + "spent_hours": 16, @@ -903,14 +1021,7 @@ const AttachmentsData = [ - "caption": "Landing page mockups v1", - - - - - - - "uploaded_on": new Date('2025-12-05T10:25:00Z'), + "completed": true, @@ -921,6 +1032,20 @@ const AttachmentsData = [ + "title": "Prepare Q2 campaign assets", + + + + + + + "description": "Design banners and messaging for campaign channels", + + + + + + // type code here for "relation_one" field @@ -935,6 +1060,55 @@ const AttachmentsData = [ + // type code here for "relation_one" field + + + + + + + "status": "InProgress", + + + + + + + "priority": "High", + + + + + + + "start_date": new Date('2025-03-15T09:00:00Z'), + + + + + + + "due_date": new Date('2025-04-15T17:00:00Z'), + + + + + + + "estimated_hours": 48, + + + + + + + "spent_hours": 0, + + + + + + // type code here for "files" field @@ -942,320 +1116,7 @@ const AttachmentsData = [ - "caption": "Auth API specification", - - - - - - - "uploaded_on": new Date('2025-11-20T14:00:00Z'), - - - - }, - - { - - - - - // type code here for "relation_one" field - - - - - - - // type code here for "relation_one" field - - - - - - - // type code here for "files" field - - - - - - - "caption": "Component demo recording", - - - - - - - "uploaded_on": new Date('2025-10-15T11:10:00Z'), - - - - }, - -]; - - - -const NotificationsData = [ - - { - - - - - // type code here for "relation_one" field - - - - - - - "message": "New review requested for Landing Page Mockups", - - - - - - - // type code here for "relation_one" field - - - - - - - "read": true, - - - - - - - "sent_at": new Date('2025-12-05T10:30:00Z'), - - - - }, - - { - - - - - // type code here for "relation_one" field - - - - - - - "message": "Please review the Auth API draft", - - - - - - - // type code here for "relation_one" field - - - - - - - "read": false, - - - - - - - "sent_at": new Date('2025-11-25T15:45:00Z'), - - - - }, - - { - - - - - // type code here for "relation_one" field - - - - - - - "message": "E2E tests are blocked in staging", - - - - - - - // type code here for "relation_one" field - - - - - - - "read": true, - - - - - - - "sent_at": new Date('2026-01-03T08:05:00Z'), - - - - }, - -]; - - - -const ReportsData = [ - - { - - - - - "title": "Website Redesign Q4 Progress", - - - - - - - // type code here for "relation_one" field - - - - - - - // type code here for "relation_one" field - - - - - - - "created_on": new Date('2026-01-01T12:00:00Z'), - - - - - - - // type code here for "files" field - - - - - - - "summary": "Progress update covering design and implementation milestones", - - - - }, - - { - - - - - "title": "Mobile App V2 Roadmap", - - - - - - - // type code here for "relation_one" field - - - - - - - // type code here for "relation_one" field - - - - - - - "created_on": new Date('2025-10-01T08:30:00Z'), - - - - - - - // type code here for "files" field - - - - - - - "summary": "Roadmap and major deliverables for mobile version two", - - - - }, - - { - - - - - "title": "Design System Audit", - - - - - - - // type code here for "relation_one" field - - - - - - - // type code here for "relation_one" field - - - - - - - "created_on": new Date('2025-12-01T11:45:00Z'), - - - - - - - // type code here for "files" field - - - - - - - "summary": "Audit of current components and recommended improvements", + "completed": false, @@ -1311,168 +1172,167 @@ const ReportsData = [ - - - - - - - - - async function associateTeamWithOwner() { - - const relatedOwner0 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Team0 = await Teams.findOne({ - order: [['id', 'ASC']], - offset: 0 - }); - if (Team0?.setOwner) - { - await - Team0. - setOwner(relatedOwner0); - } - - const relatedOwner1 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Team1 = await Teams.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (Team1?.setOwner) - { - await - Team1. - setOwner(relatedOwner1); - } - - const relatedOwner2 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Team2 = await Teams.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Team2?.setOwner) - { - await - Team2. - setOwner(relatedOwner2); - } - - } - - + + - async function associateTeamMemberWithTeam() { + async function associateTeamMembershipWithTeam() { const relatedTeam0 = await Teams.findOne({ offset: Math.floor(Math.random() * (await Teams.count())), }); - const TeamMember0 = await TeamMembers.findOne({ + const TeamMembership0 = await TeamMemberships.findOne({ order: [['id', 'ASC']], offset: 0 }); - if (TeamMember0?.setTeam) + if (TeamMembership0?.setTeam) { await - TeamMember0. + TeamMembership0. setTeam(relatedTeam0); } const relatedTeam1 = await Teams.findOne({ offset: Math.floor(Math.random() * (await Teams.count())), }); - const TeamMember1 = await TeamMembers.findOne({ + const TeamMembership1 = await TeamMemberships.findOne({ order: [['id', 'ASC']], offset: 1 }); - if (TeamMember1?.setTeam) + if (TeamMembership1?.setTeam) { await - TeamMember1. + TeamMembership1. setTeam(relatedTeam1); } const relatedTeam2 = await Teams.findOne({ offset: Math.floor(Math.random() * (await Teams.count())), }); - const TeamMember2 = await TeamMembers.findOne({ + const TeamMembership2 = await TeamMemberships.findOne({ order: [['id', 'ASC']], offset: 2 }); - if (TeamMember2?.setTeam) + if (TeamMembership2?.setTeam) { await - TeamMember2. + TeamMembership2. setTeam(relatedTeam2); } + const relatedTeam3 = await Teams.findOne({ + offset: Math.floor(Math.random() * (await Teams.count())), + }); + const TeamMembership3 = await TeamMemberships.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (TeamMembership3?.setTeam) + { + await + TeamMembership3. + setTeam(relatedTeam3); + } + + const relatedTeam4 = await Teams.findOne({ + offset: Math.floor(Math.random() * (await Teams.count())), + }); + const TeamMembership4 = await TeamMemberships.findOne({ + order: [['id', 'ASC']], + offset: 4 + }); + if (TeamMembership4?.setTeam) + { + await + TeamMembership4. + setTeam(relatedTeam4); + } + } - async function associateTeamMemberWithUser() { + async function associateTeamMembershipWithUser() { const relatedUser0 = await Users.findOne({ offset: Math.floor(Math.random() * (await Users.count())), }); - const TeamMember0 = await TeamMembers.findOne({ + const TeamMembership0 = await TeamMemberships.findOne({ order: [['id', 'ASC']], offset: 0 }); - if (TeamMember0?.setUser) + if (TeamMembership0?.setUser) { await - TeamMember0. + TeamMembership0. setUser(relatedUser0); } const relatedUser1 = await Users.findOne({ offset: Math.floor(Math.random() * (await Users.count())), }); - const TeamMember1 = await TeamMembers.findOne({ + const TeamMembership1 = await TeamMemberships.findOne({ order: [['id', 'ASC']], offset: 1 }); - if (TeamMember1?.setUser) + if (TeamMembership1?.setUser) { await - TeamMember1. + TeamMembership1. setUser(relatedUser1); } const relatedUser2 = await Users.findOne({ offset: Math.floor(Math.random() * (await Users.count())), }); - const TeamMember2 = await TeamMembers.findOne({ + const TeamMembership2 = await TeamMemberships.findOne({ order: [['id', 'ASC']], offset: 2 }); - if (TeamMember2?.setUser) + if (TeamMembership2?.setUser) { await - TeamMember2. + TeamMembership2. setUser(relatedUser2); } + const relatedUser3 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const TeamMembership3 = await TeamMemberships.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (TeamMembership3?.setUser) + { + await + TeamMembership3. + setUser(relatedUser3); + } + + const relatedUser4 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const TeamMembership4 = await TeamMemberships.findOne({ + order: [['id', 'ASC']], + offset: 4 + }); + if (TeamMembership4?.setUser) + { + await + TeamMembership4. + setUser(relatedUser4); + } + } - - - - @@ -1480,12 +1340,6 @@ const ReportsData = [ - - - - - - @@ -1534,6 +1388,34 @@ const ReportsData = [ setOwner(relatedOwner2); } + const relatedOwner3 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Project3 = await Projects.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (Project3?.setOwner) + { + await + Project3. + setOwner(relatedOwner3); + } + + const relatedOwner4 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Project4 = await Projects.findOne({ + order: [['id', 'ASC']], + offset: 4 + }); + if (Project4?.setOwner) + { + await + Project4. + setOwner(relatedOwner4); + } + } @@ -1583,6 +1465,34 @@ const ReportsData = [ setTeam(relatedTeam2); } + const relatedTeam3 = await Teams.findOne({ + offset: Math.floor(Math.random() * (await Teams.count())), + }); + const Project3 = await Projects.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (Project3?.setTeam) + { + await + Project3. + setTeam(relatedTeam3); + } + + const relatedTeam4 = await Teams.findOne({ + offset: Math.floor(Math.random() * (await Teams.count())), + }); + const Project4 = await Projects.findOne({ + order: [['id', 'ASC']], + offset: 4 + }); + if (Project4?.setTeam) + { + await + Project4. + setTeam(relatedTeam4); + } + } @@ -1590,6 +1500,12 @@ const ReportsData = [ + + + + + + @@ -1599,7 +1515,80 @@ const ReportsData = [ - + + async function associateTaskWithProject() { + + const relatedProject0 = await Projects.findOne({ + offset: Math.floor(Math.random() * (await Projects.count())), + }); + const Task0 = await Tasks.findOne({ + order: [['id', 'ASC']], + offset: 0 + }); + if (Task0?.setProject) + { + await + Task0. + setProject(relatedProject0); + } + + const relatedProject1 = await Projects.findOne({ + offset: Math.floor(Math.random() * (await Projects.count())), + }); + const Task1 = await Tasks.findOne({ + order: [['id', 'ASC']], + offset: 1 + }); + if (Task1?.setProject) + { + await + Task1. + setProject(relatedProject1); + } + + const relatedProject2 = await Projects.findOne({ + offset: Math.floor(Math.random() * (await Projects.count())), + }); + const Task2 = await Tasks.findOne({ + order: [['id', 'ASC']], + offset: 2 + }); + if (Task2?.setProject) + { + await + Task2. + setProject(relatedProject2); + } + + const relatedProject3 = await Projects.findOne({ + offset: Math.floor(Math.random() * (await Projects.count())), + }); + const Task3 = await Tasks.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (Task3?.setProject) + { + await + Task3. + setProject(relatedProject3); + } + + const relatedProject4 = await Projects.findOne({ + offset: Math.floor(Math.random() * (await Projects.count())), + }); + const Task4 = await Tasks.findOne({ + order: [['id', 'ASC']], + offset: 4 + }); + if (Task4?.setProject) + { + await + Task4. + setProject(relatedProject4); + } + + } @@ -1648,6 +1637,34 @@ const ReportsData = [ setAssignee(relatedAssignee2); } + const relatedAssignee3 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Task3 = await Tasks.findOne({ + order: [['id', 'ASC']], + offset: 3 + }); + if (Task3?.setAssignee) + { + await + Task3. + setAssignee(relatedAssignee3); + } + + const relatedAssignee4 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), + }); + const Task4 = await Tasks.findOne({ + order: [['id', 'ASC']], + offset: 4 + }); + if (Task4?.setAssignee) + { + await + Task4. + setAssignee(relatedAssignee4); + } + } @@ -1697,53 +1714,32 @@ const ReportsData = [ setReporter(relatedReporter2); } - } - - - - - async function associateTaskWithProject() { - - const relatedProject0 = await Projects.findOne({ - offset: Math.floor(Math.random() * (await Projects.count())), + const relatedReporter3 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), }); - const Task0 = await Tasks.findOne({ + const Task3 = await Tasks.findOne({ order: [['id', 'ASC']], - offset: 0 + offset: 3 }); - if (Task0?.setProject) + if (Task3?.setReporter) { await - Task0. - setProject(relatedProject0); + Task3. + setReporter(relatedReporter3); } - const relatedProject1 = await Projects.findOne({ - offset: Math.floor(Math.random() * (await Projects.count())), + const relatedReporter4 = await Users.findOne({ + offset: Math.floor(Math.random() * (await Users.count())), }); - const Task1 = await Tasks.findOne({ + const Task4 = await Tasks.findOne({ order: [['id', 'ASC']], - offset: 1 + offset: 4 }); - if (Task1?.setProject) + if (Task4?.setReporter) { await - Task1. - setProject(relatedProject1); - } - - const relatedProject2 = await Projects.findOne({ - offset: Math.floor(Math.random() * (await Projects.count())), - }); - const Task2 = await Tasks.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Task2?.setProject) - { - await - Task2. - setProject(relatedProject2); + Task4. + setReporter(relatedReporter4); } } @@ -1757,430 +1753,6 @@ const ReportsData = [ - - - - - - - async function associateCommentWithTask() { - - const relatedTask0 = await Tasks.findOne({ - offset: Math.floor(Math.random() * (await Tasks.count())), - }); - const Comment0 = await Comments.findOne({ - order: [['id', 'ASC']], - offset: 0 - }); - if (Comment0?.setTask) - { - await - Comment0. - setTask(relatedTask0); - } - - const relatedTask1 = await Tasks.findOne({ - offset: Math.floor(Math.random() * (await Tasks.count())), - }); - const Comment1 = await Comments.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (Comment1?.setTask) - { - await - Comment1. - setTask(relatedTask1); - } - - const relatedTask2 = await Tasks.findOne({ - offset: Math.floor(Math.random() * (await Tasks.count())), - }); - const Comment2 = await Comments.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Comment2?.setTask) - { - await - Comment2. - setTask(relatedTask2); - } - - } - - - - - async function associateCommentWithAuthor() { - - const relatedAuthor0 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Comment0 = await Comments.findOne({ - order: [['id', 'ASC']], - offset: 0 - }); - if (Comment0?.setAuthor) - { - await - Comment0. - setAuthor(relatedAuthor0); - } - - const relatedAuthor1 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Comment1 = await Comments.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (Comment1?.setAuthor) - { - await - Comment1. - setAuthor(relatedAuthor1); - } - - const relatedAuthor2 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Comment2 = await Comments.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Comment2?.setAuthor) - { - await - Comment2. - setAuthor(relatedAuthor2); - } - - } - - - - - - - - - - - - - async function associateAttachmentWithTask() { - - const relatedTask0 = await Tasks.findOne({ - offset: Math.floor(Math.random() * (await Tasks.count())), - }); - const Attachment0 = await Attachments.findOne({ - order: [['id', 'ASC']], - offset: 0 - }); - if (Attachment0?.setTask) - { - await - Attachment0. - setTask(relatedTask0); - } - - const relatedTask1 = await Tasks.findOne({ - offset: Math.floor(Math.random() * (await Tasks.count())), - }); - const Attachment1 = await Attachments.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (Attachment1?.setTask) - { - await - Attachment1. - setTask(relatedTask1); - } - - const relatedTask2 = await Tasks.findOne({ - offset: Math.floor(Math.random() * (await Tasks.count())), - }); - const Attachment2 = await Attachments.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Attachment2?.setTask) - { - await - Attachment2. - setTask(relatedTask2); - } - - } - - - - - async function associateAttachmentWithUploaded_by() { - - const relatedUploaded_by0 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Attachment0 = await Attachments.findOne({ - order: [['id', 'ASC']], - offset: 0 - }); - if (Attachment0?.setUploaded_by) - { - await - Attachment0. - setUploaded_by(relatedUploaded_by0); - } - - const relatedUploaded_by1 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Attachment1 = await Attachments.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (Attachment1?.setUploaded_by) - { - await - Attachment1. - setUploaded_by(relatedUploaded_by1); - } - - const relatedUploaded_by2 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Attachment2 = await Attachments.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Attachment2?.setUploaded_by) - { - await - Attachment2. - setUploaded_by(relatedUploaded_by2); - } - - } - - - - - - - - - - - - - - - async function associateNotificationWithRecipient() { - - const relatedRecipient0 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Notification0 = await Notifications.findOne({ - order: [['id', 'ASC']], - offset: 0 - }); - if (Notification0?.setRecipient) - { - await - Notification0. - setRecipient(relatedRecipient0); - } - - const relatedRecipient1 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Notification1 = await Notifications.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (Notification1?.setRecipient) - { - await - Notification1. - setRecipient(relatedRecipient1); - } - - const relatedRecipient2 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Notification2 = await Notifications.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Notification2?.setRecipient) - { - await - Notification2. - setRecipient(relatedRecipient2); - } - - } - - - - - - - async function associateNotificationWithRelated_task() { - - const relatedRelated_task0 = await Tasks.findOne({ - offset: Math.floor(Math.random() * (await Tasks.count())), - }); - const Notification0 = await Notifications.findOne({ - order: [['id', 'ASC']], - offset: 0 - }); - if (Notification0?.setRelated_task) - { - await - Notification0. - setRelated_task(relatedRelated_task0); - } - - const relatedRelated_task1 = await Tasks.findOne({ - offset: Math.floor(Math.random() * (await Tasks.count())), - }); - const Notification1 = await Notifications.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (Notification1?.setRelated_task) - { - await - Notification1. - setRelated_task(relatedRelated_task1); - } - - const relatedRelated_task2 = await Tasks.findOne({ - offset: Math.floor(Math.random() * (await Tasks.count())), - }); - const Notification2 = await Notifications.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Notification2?.setRelated_task) - { - await - Notification2. - setRelated_task(relatedRelated_task2); - } - - } - - - - - - - - - - - - - - - async function associateReportWithProject() { - - const relatedProject0 = await Projects.findOne({ - offset: Math.floor(Math.random() * (await Projects.count())), - }); - const Report0 = await Reports.findOne({ - order: [['id', 'ASC']], - offset: 0 - }); - if (Report0?.setProject) - { - await - Report0. - setProject(relatedProject0); - } - - const relatedProject1 = await Projects.findOne({ - offset: Math.floor(Math.random() * (await Projects.count())), - }); - const Report1 = await Reports.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (Report1?.setProject) - { - await - Report1. - setProject(relatedProject1); - } - - const relatedProject2 = await Projects.findOne({ - offset: Math.floor(Math.random() * (await Projects.count())), - }); - const Report2 = await Reports.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Report2?.setProject) - { - await - Report2. - setProject(relatedProject2); - } - - } - - - - - async function associateReportWithGenerated_by() { - - const relatedGenerated_by0 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Report0 = await Reports.findOne({ - order: [['id', 'ASC']], - offset: 0 - }); - if (Report0?.setGenerated_by) - { - await - Report0. - setGenerated_by(relatedGenerated_by0); - } - - const relatedGenerated_by1 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Report1 = await Reports.findOne({ - order: [['id', 'ASC']], - offset: 1 - }); - if (Report1?.setGenerated_by) - { - await - Report1. - setGenerated_by(relatedGenerated_by1); - } - - const relatedGenerated_by2 = await Users.findOne({ - offset: Math.floor(Math.random() * (await Users.count())), - }); - const Report2 = await Reports.findOne({ - order: [['id', 'ASC']], - offset: 2 - }); - if (Report2?.setGenerated_by) - { - await - Report2. - setGenerated_by(relatedGenerated_by2); - } - - } @@ -2205,7 +1777,7 @@ module.exports = { - await TeamMembers.bulkCreate(TeamMembersData); + await TeamMemberships.bulkCreate(TeamMembershipsData); @@ -2218,26 +1790,6 @@ module.exports = { await Tasks.bulkCreate(TasksData); - - - await Comments.bulkCreate(CommentsData); - - - - - await Attachments.bulkCreate(AttachmentsData); - - - - - await Notifications.bulkCreate(NotificationsData); - - - - - await Reports.bulkCreate(ReportsData); - - await Promise.all([ @@ -2283,44 +1835,25 @@ module.exports = { - - - - - - - - - await associateTeamWithOwner(), - - - - - - - - await associateTeamMemberWithTeam(), - - - - - await associateTeamMemberWithUser(), - - - - - - - - + + await associateTeamMembershipWithTeam(), + + + await associateTeamMembershipWithUser(), + + + + + + @@ -2340,6 +1873,12 @@ module.exports = { + + + + + + @@ -2348,7 +1887,8 @@ module.exports = { - + + await associateTaskWithProject(), @@ -2362,9 +1902,6 @@ module.exports = { - - await associateTaskWithProject(), - @@ -2374,76 +1911,6 @@ module.exports = { - - - - - - await associateCommentWithTask(), - - - - - await associateCommentWithAuthor(), - - - - - - - - - - - - await associateAttachmentWithTask(), - - - - - await associateAttachmentWithUploaded_by(), - - - - - - - - - - - - - - await associateNotificationWithRecipient(), - - - - - - - await associateNotificationWithRelated_task(), - - - - - - - - - - - - - - await associateReportWithProject(), - - - - - await associateReportWithGenerated_by(), - - @@ -2465,7 +1932,7 @@ module.exports = { await queryInterface.bulkDelete('teams', null, {}); - await queryInterface.bulkDelete('team_members', null, {}); + await queryInterface.bulkDelete('team_memberships', null, {}); await queryInterface.bulkDelete('projects', null, {}); @@ -2473,18 +1940,6 @@ module.exports = { await queryInterface.bulkDelete('tasks', null, {}); - - await queryInterface.bulkDelete('comments', null, {}); - - - await queryInterface.bulkDelete('attachments', null, {}); - - - await queryInterface.bulkDelete('notifications', null, {}); - - - await queryInterface.bulkDelete('reports', null, {}); - }, }; \ No newline at end of file diff --git a/backend/src/index.js b/backend/src/index.js index 6c7d842..11701d3 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -16,12 +16,10 @@ const fileRoutes = require('./routes/file'); const searchRoutes = require('./routes/search'); const pexelsRoutes = require('./routes/pexels'); - const openaiRoutes = require('./routes/openai'); - const usersRoutes = require('./routes/users'); const rolesRoutes = require('./routes/roles'); @@ -30,20 +28,12 @@ const permissionsRoutes = require('./routes/permissions'); const teamsRoutes = require('./routes/teams'); -const team_membersRoutes = require('./routes/team_members'); +const team_membershipsRoutes = require('./routes/team_memberships'); const projectsRoutes = require('./routes/projects'); const tasksRoutes = require('./routes/tasks'); -const commentsRoutes = require('./routes/comments'); - -const attachmentsRoutes = require('./routes/attachments'); - -const notificationsRoutes = require('./routes/notifications'); - -const reportsRoutes = require('./routes/reports'); - const getBaseUrl = (url) => { if (!url) return ''; @@ -55,8 +45,8 @@ const options = { openapi: "3.0.0", info: { version: "1.0.0", - title: "Team Projects Hub", - description: "Team Projects Hub Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.", + title: "TeamFlow Manager", + description: "TeamFlow Manager Online REST API for Testing and Prototyping application. You can perform all major operations with your entities - create, delete and etc.", }, servers: [ { @@ -110,27 +100,22 @@ app.use('/api/permissions', passport.authenticate('jwt', {session: false}), perm app.use('/api/teams', passport.authenticate('jwt', {session: false}), teamsRoutes); -app.use('/api/team_members', passport.authenticate('jwt', {session: false}), team_membersRoutes); +app.use('/api/team_memberships', passport.authenticate('jwt', {session: false}), team_membershipsRoutes); app.use('/api/projects', passport.authenticate('jwt', {session: false}), projectsRoutes); app.use('/api/tasks', passport.authenticate('jwt', {session: false}), tasksRoutes); -app.use('/api/comments', passport.authenticate('jwt', {session: false}), commentsRoutes); - -app.use('/api/attachments', passport.authenticate('jwt', {session: false}), attachmentsRoutes); - -app.use('/api/notifications', passport.authenticate('jwt', {session: false}), notificationsRoutes); - -app.use('/api/reports', passport.authenticate('jwt', {session: false}), reportsRoutes); - - app.use( '/api/openai', passport.authenticate('jwt', { session: false }), openaiRoutes, ); - +app.use( + '/api/ai', + passport.authenticate('jwt', { session: false }), + openaiRoutes, +); app.use( '/api/search', diff --git a/backend/src/routes/attachments.js b/backend/src/routes/attachments.js deleted file mode 100644 index 0b4d720..0000000 --- a/backend/src/routes/attachments.js +++ /dev/null @@ -1,426 +0,0 @@ - -const express = require('express'); - -const AttachmentsService = require('../services/attachments'); -const AttachmentsDBApi = require('../db/api/attachments'); -const wrapAsync = require('../helpers').wrapAsync; - - -const router = express.Router(); - -const { parse } = require('json2csv'); - - -const { - checkCrudPermissions, -} = require('../middlewares/check-permissions'); - -router.use(checkCrudPermissions('attachments')); - - -/** - * @swagger - * components: - * schemas: - * Attachments: - * type: object - * properties: - - - - - */ - -/** - * @swagger - * tags: - * name: Attachments - * description: The Attachments managing API - */ - -/** -* @swagger -* /api/attachments: -* post: -* security: -* - bearerAuth: [] -* tags: [Attachments] -* summary: Add new item -* description: Add new item -* requestBody: -* required: true -* content: -* application/json: -* schema: -* properties: -* data: -* description: Data of the updated item -* type: object -* $ref: "#/components/schemas/Attachments" -* responses: -* 200: -* description: The item was successfully added -* content: -* application/json: -* schema: -* $ref: "#/components/schemas/Attachments" -* 401: -* $ref: "#/components/responses/UnauthorizedError" -* 405: -* description: Invalid input data -* 500: -* description: Some server error -*/ -router.post('/', wrapAsync(async (req, res) => { - const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; - const link = new URL(referer); - await AttachmentsService.create(req.body.data, req.currentUser, true, link.host); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/budgets/bulk-import: - * post: - * security: - * - bearerAuth: [] - * tags: [Attachments] - * summary: Bulk import items - * description: Bulk import items - * requestBody: - * required: true - * content: - * application/json: - * schema: - * properties: - * data: - * description: Data of the updated items - * type: array - * items: - * $ref: "#/components/schemas/Attachments" - * responses: - * 200: - * description: The items were successfully imported - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Attachments" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 405: - * description: Invalid input data - * 500: - * description: Some server error - * - */ -router.post('/bulk-import', wrapAsync(async (req, res) => { - const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; - const link = new URL(referer); - await AttachmentsService.bulkImport(req, res, true, link.host); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/attachments/{id}: - * put: - * security: - * - bearerAuth: [] - * tags: [Attachments] - * summary: Update the data of the selected item - * description: Update the data of the selected item - * parameters: - * - in: path - * name: id - * description: Item ID to update - * required: true - * schema: - * type: string - * requestBody: - * description: Set new item data - * required: true - * content: - * application/json: - * schema: - * properties: - * id: - * description: ID of the updated item - * type: string - * data: - * description: Data of the updated item - * type: object - * $ref: "#/components/schemas/Attachments" - * required: - * - id - * responses: - * 200: - * description: The item data was successfully updated - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Attachments" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.put('/:id', wrapAsync(async (req, res) => { - await AttachmentsService.update(req.body.data, req.body.id, req.currentUser); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/attachments/{id}: - * delete: - * security: - * - bearerAuth: [] - * tags: [Attachments] - * summary: Delete the selected item - * description: Delete the selected item - * parameters: - * - in: path - * name: id - * description: Item ID to delete - * required: true - * schema: - * type: string - * responses: - * 200: - * description: The item was successfully deleted - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Attachments" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.delete('/:id', wrapAsync(async (req, res) => { - await AttachmentsService.remove(req.params.id, req.currentUser); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/attachments/deleteByIds: - * post: - * security: - * - bearerAuth: [] - * tags: [Attachments] - * summary: Delete the selected item list - * description: Delete the selected item list - * requestBody: - * required: true - * content: - * application/json: - * schema: - * properties: - * ids: - * description: IDs of the updated items - * type: array - * responses: - * 200: - * description: The items was successfully deleted - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Attachments" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Items not found - * 500: - * description: Some server error - */ -router.post('/deleteByIds', wrapAsync(async (req, res) => { - await AttachmentsService.deleteByIds(req.body.data, req.currentUser); - const payload = true; - res.status(200).send(payload); - })); - -/** - * @swagger - * /api/attachments: - * get: - * security: - * - bearerAuth: [] - * tags: [Attachments] - * summary: Get all attachments - * description: Get all attachments - * responses: - * 200: - * description: Attachments list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Attachments" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error -*/ -router.get('/', wrapAsync(async (req, res) => { - const filetype = req.query.filetype - - const currentUser = req.currentUser; - const payload = await AttachmentsDBApi.findAll( - req.query, { currentUser } - ); - if (filetype && filetype === 'csv') { - const fields = ['id', - - - - ]; - const opts = { fields }; - try { - const csv = parse(payload.rows, opts); - res.status(200).attachment(csv); - res.send(csv) - - } catch (err) { - console.error(err); - } - } else { - res.status(200).send(payload); - } - -})); - -/** - * @swagger - * /api/attachments/count: - * get: - * security: - * - bearerAuth: [] - * tags: [Attachments] - * summary: Count all attachments - * description: Count all attachments - * responses: - * 200: - * description: Attachments count successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Attachments" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/count', wrapAsync(async (req, res) => { - - const currentUser = req.currentUser; - const payload = await AttachmentsDBApi.findAll( - req.query, - null, - { countOnly: true, currentUser } - ); - - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/attachments/autocomplete: - * get: - * security: - * - bearerAuth: [] - * tags: [Attachments] - * summary: Find all attachments that match search criteria - * description: Find all attachments that match search criteria - * responses: - * 200: - * description: Attachments list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Attachments" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/autocomplete', async (req, res) => { - - const payload = await AttachmentsDBApi.findAllAutocomplete( - req.query.query, - req.query.limit, - req.query.offset, - - ); - - res.status(200).send(payload); -}); - -/** - * @swagger - * /api/attachments/{id}: - * get: - * security: - * - bearerAuth: [] - * tags: [Attachments] - * summary: Get selected item - * description: Get selected item - * parameters: - * - in: path - * name: id - * description: ID of item to get - * required: true - * schema: - * type: string - * responses: - * 200: - * description: Selected item successfully received - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Attachments" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.get('/:id', wrapAsync(async (req, res) => { - const payload = await AttachmentsDBApi.findBy( - { id: req.params.id }, - ); - - - - res.status(200).send(payload); -})); - -router.use('/', require('../helpers').commonErrorHandler); - -module.exports = router; diff --git a/backend/src/routes/comments.js b/backend/src/routes/comments.js deleted file mode 100644 index 3e971fe..0000000 --- a/backend/src/routes/comments.js +++ /dev/null @@ -1,426 +0,0 @@ - -const express = require('express'); - -const CommentsService = require('../services/comments'); -const CommentsDBApi = require('../db/api/comments'); -const wrapAsync = require('../helpers').wrapAsync; - - -const router = express.Router(); - -const { parse } = require('json2csv'); - - -const { - checkCrudPermissions, -} = require('../middlewares/check-permissions'); - -router.use(checkCrudPermissions('comments')); - - -/** - * @swagger - * components: - * schemas: - * Comments: - * type: object - * properties: - - - - - */ - -/** - * @swagger - * tags: - * name: Comments - * description: The Comments managing API - */ - -/** -* @swagger -* /api/comments: -* post: -* security: -* - bearerAuth: [] -* tags: [Comments] -* summary: Add new item -* description: Add new item -* requestBody: -* required: true -* content: -* application/json: -* schema: -* properties: -* data: -* description: Data of the updated item -* type: object -* $ref: "#/components/schemas/Comments" -* responses: -* 200: -* description: The item was successfully added -* content: -* application/json: -* schema: -* $ref: "#/components/schemas/Comments" -* 401: -* $ref: "#/components/responses/UnauthorizedError" -* 405: -* description: Invalid input data -* 500: -* description: Some server error -*/ -router.post('/', wrapAsync(async (req, res) => { - const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; - const link = new URL(referer); - await CommentsService.create(req.body.data, req.currentUser, true, link.host); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/budgets/bulk-import: - * post: - * security: - * - bearerAuth: [] - * tags: [Comments] - * summary: Bulk import items - * description: Bulk import items - * requestBody: - * required: true - * content: - * application/json: - * schema: - * properties: - * data: - * description: Data of the updated items - * type: array - * items: - * $ref: "#/components/schemas/Comments" - * responses: - * 200: - * description: The items were successfully imported - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Comments" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 405: - * description: Invalid input data - * 500: - * description: Some server error - * - */ -router.post('/bulk-import', wrapAsync(async (req, res) => { - const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; - const link = new URL(referer); - await CommentsService.bulkImport(req, res, true, link.host); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/comments/{id}: - * put: - * security: - * - bearerAuth: [] - * tags: [Comments] - * summary: Update the data of the selected item - * description: Update the data of the selected item - * parameters: - * - in: path - * name: id - * description: Item ID to update - * required: true - * schema: - * type: string - * requestBody: - * description: Set new item data - * required: true - * content: - * application/json: - * schema: - * properties: - * id: - * description: ID of the updated item - * type: string - * data: - * description: Data of the updated item - * type: object - * $ref: "#/components/schemas/Comments" - * required: - * - id - * responses: - * 200: - * description: The item data was successfully updated - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Comments" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.put('/:id', wrapAsync(async (req, res) => { - await CommentsService.update(req.body.data, req.body.id, req.currentUser); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/comments/{id}: - * delete: - * security: - * - bearerAuth: [] - * tags: [Comments] - * summary: Delete the selected item - * description: Delete the selected item - * parameters: - * - in: path - * name: id - * description: Item ID to delete - * required: true - * schema: - * type: string - * responses: - * 200: - * description: The item was successfully deleted - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Comments" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.delete('/:id', wrapAsync(async (req, res) => { - await CommentsService.remove(req.params.id, req.currentUser); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/comments/deleteByIds: - * post: - * security: - * - bearerAuth: [] - * tags: [Comments] - * summary: Delete the selected item list - * description: Delete the selected item list - * requestBody: - * required: true - * content: - * application/json: - * schema: - * properties: - * ids: - * description: IDs of the updated items - * type: array - * responses: - * 200: - * description: The items was successfully deleted - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Comments" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Items not found - * 500: - * description: Some server error - */ -router.post('/deleteByIds', wrapAsync(async (req, res) => { - await CommentsService.deleteByIds(req.body.data, req.currentUser); - const payload = true; - res.status(200).send(payload); - })); - -/** - * @swagger - * /api/comments: - * get: - * security: - * - bearerAuth: [] - * tags: [Comments] - * summary: Get all comments - * description: Get all comments - * responses: - * 200: - * description: Comments list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Comments" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error -*/ -router.get('/', wrapAsync(async (req, res) => { - const filetype = req.query.filetype - - const currentUser = req.currentUser; - const payload = await CommentsDBApi.findAll( - req.query, { currentUser } - ); - if (filetype && filetype === 'csv') { - const fields = ['id', - - - - ]; - const opts = { fields }; - try { - const csv = parse(payload.rows, opts); - res.status(200).attachment(csv); - res.send(csv) - - } catch (err) { - console.error(err); - } - } else { - res.status(200).send(payload); - } - -})); - -/** - * @swagger - * /api/comments/count: - * get: - * security: - * - bearerAuth: [] - * tags: [Comments] - * summary: Count all comments - * description: Count all comments - * responses: - * 200: - * description: Comments count successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Comments" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/count', wrapAsync(async (req, res) => { - - const currentUser = req.currentUser; - const payload = await CommentsDBApi.findAll( - req.query, - null, - { countOnly: true, currentUser } - ); - - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/comments/autocomplete: - * get: - * security: - * - bearerAuth: [] - * tags: [Comments] - * summary: Find all comments that match search criteria - * description: Find all comments that match search criteria - * responses: - * 200: - * description: Comments list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Comments" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/autocomplete', async (req, res) => { - - const payload = await CommentsDBApi.findAllAutocomplete( - req.query.query, - req.query.limit, - req.query.offset, - - ); - - res.status(200).send(payload); -}); - -/** - * @swagger - * /api/comments/{id}: - * get: - * security: - * - bearerAuth: [] - * tags: [Comments] - * summary: Get selected item - * description: Get selected item - * parameters: - * - in: path - * name: id - * description: ID of item to get - * required: true - * schema: - * type: string - * responses: - * 200: - * description: Selected item successfully received - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Comments" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.get('/:id', wrapAsync(async (req, res) => { - const payload = await CommentsDBApi.findBy( - { id: req.params.id }, - ); - - - - res.status(200).send(payload); -})); - -router.use('/', require('../helpers').commonErrorHandler); - -module.exports = router; diff --git a/backend/src/routes/notifications.js b/backend/src/routes/notifications.js deleted file mode 100644 index 60625d6..0000000 --- a/backend/src/routes/notifications.js +++ /dev/null @@ -1,426 +0,0 @@ - -const express = require('express'); - -const NotificationsService = require('../services/notifications'); -const NotificationsDBApi = require('../db/api/notifications'); -const wrapAsync = require('../helpers').wrapAsync; - - -const router = express.Router(); - -const { parse } = require('json2csv'); - - -const { - checkCrudPermissions, -} = require('../middlewares/check-permissions'); - -router.use(checkCrudPermissions('notifications')); - - -/** - * @swagger - * components: - * schemas: - * Notifications: - * type: object - * properties: - - - - - */ - -/** - * @swagger - * tags: - * name: Notifications - * description: The Notifications managing API - */ - -/** -* @swagger -* /api/notifications: -* post: -* security: -* - bearerAuth: [] -* tags: [Notifications] -* summary: Add new item -* description: Add new item -* requestBody: -* required: true -* content: -* application/json: -* schema: -* properties: -* data: -* description: Data of the updated item -* type: object -* $ref: "#/components/schemas/Notifications" -* responses: -* 200: -* description: The item was successfully added -* content: -* application/json: -* schema: -* $ref: "#/components/schemas/Notifications" -* 401: -* $ref: "#/components/responses/UnauthorizedError" -* 405: -* description: Invalid input data -* 500: -* description: Some server error -*/ -router.post('/', wrapAsync(async (req, res) => { - const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; - const link = new URL(referer); - await NotificationsService.create(req.body.data, req.currentUser, true, link.host); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/budgets/bulk-import: - * post: - * security: - * - bearerAuth: [] - * tags: [Notifications] - * summary: Bulk import items - * description: Bulk import items - * requestBody: - * required: true - * content: - * application/json: - * schema: - * properties: - * data: - * description: Data of the updated items - * type: array - * items: - * $ref: "#/components/schemas/Notifications" - * responses: - * 200: - * description: The items were successfully imported - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Notifications" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 405: - * description: Invalid input data - * 500: - * description: Some server error - * - */ -router.post('/bulk-import', wrapAsync(async (req, res) => { - const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; - const link = new URL(referer); - await NotificationsService.bulkImport(req, res, true, link.host); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/notifications/{id}: - * put: - * security: - * - bearerAuth: [] - * tags: [Notifications] - * summary: Update the data of the selected item - * description: Update the data of the selected item - * parameters: - * - in: path - * name: id - * description: Item ID to update - * required: true - * schema: - * type: string - * requestBody: - * description: Set new item data - * required: true - * content: - * application/json: - * schema: - * properties: - * id: - * description: ID of the updated item - * type: string - * data: - * description: Data of the updated item - * type: object - * $ref: "#/components/schemas/Notifications" - * required: - * - id - * responses: - * 200: - * description: The item data was successfully updated - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Notifications" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.put('/:id', wrapAsync(async (req, res) => { - await NotificationsService.update(req.body.data, req.body.id, req.currentUser); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/notifications/{id}: - * delete: - * security: - * - bearerAuth: [] - * tags: [Notifications] - * summary: Delete the selected item - * description: Delete the selected item - * parameters: - * - in: path - * name: id - * description: Item ID to delete - * required: true - * schema: - * type: string - * responses: - * 200: - * description: The item was successfully deleted - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Notifications" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.delete('/:id', wrapAsync(async (req, res) => { - await NotificationsService.remove(req.params.id, req.currentUser); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/notifications/deleteByIds: - * post: - * security: - * - bearerAuth: [] - * tags: [Notifications] - * summary: Delete the selected item list - * description: Delete the selected item list - * requestBody: - * required: true - * content: - * application/json: - * schema: - * properties: - * ids: - * description: IDs of the updated items - * type: array - * responses: - * 200: - * description: The items was successfully deleted - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Notifications" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Items not found - * 500: - * description: Some server error - */ -router.post('/deleteByIds', wrapAsync(async (req, res) => { - await NotificationsService.deleteByIds(req.body.data, req.currentUser); - const payload = true; - res.status(200).send(payload); - })); - -/** - * @swagger - * /api/notifications: - * get: - * security: - * - bearerAuth: [] - * tags: [Notifications] - * summary: Get all notifications - * description: Get all notifications - * responses: - * 200: - * description: Notifications list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Notifications" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error -*/ -router.get('/', wrapAsync(async (req, res) => { - const filetype = req.query.filetype - - const currentUser = req.currentUser; - const payload = await NotificationsDBApi.findAll( - req.query, { currentUser } - ); - if (filetype && filetype === 'csv') { - const fields = ['id', - - - - ]; - const opts = { fields }; - try { - const csv = parse(payload.rows, opts); - res.status(200).attachment(csv); - res.send(csv) - - } catch (err) { - console.error(err); - } - } else { - res.status(200).send(payload); - } - -})); - -/** - * @swagger - * /api/notifications/count: - * get: - * security: - * - bearerAuth: [] - * tags: [Notifications] - * summary: Count all notifications - * description: Count all notifications - * responses: - * 200: - * description: Notifications count successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Notifications" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/count', wrapAsync(async (req, res) => { - - const currentUser = req.currentUser; - const payload = await NotificationsDBApi.findAll( - req.query, - null, - { countOnly: true, currentUser } - ); - - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/notifications/autocomplete: - * get: - * security: - * - bearerAuth: [] - * tags: [Notifications] - * summary: Find all notifications that match search criteria - * description: Find all notifications that match search criteria - * responses: - * 200: - * description: Notifications list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Notifications" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/autocomplete', async (req, res) => { - - const payload = await NotificationsDBApi.findAllAutocomplete( - req.query.query, - req.query.limit, - req.query.offset, - - ); - - res.status(200).send(payload); -}); - -/** - * @swagger - * /api/notifications/{id}: - * get: - * security: - * - bearerAuth: [] - * tags: [Notifications] - * summary: Get selected item - * description: Get selected item - * parameters: - * - in: path - * name: id - * description: ID of item to get - * required: true - * schema: - * type: string - * responses: - * 200: - * description: Selected item successfully received - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Notifications" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.get('/:id', wrapAsync(async (req, res) => { - const payload = await NotificationsDBApi.findBy( - { id: req.params.id }, - ); - - - - res.status(200).send(payload); -})); - -router.use('/', require('../helpers').commonErrorHandler); - -module.exports = router; diff --git a/backend/src/routes/openai.js b/backend/src/routes/openai.js index 6fa17ba..2d47d9f 100644 --- a/backend/src/routes/openai.js +++ b/backend/src/routes/openai.js @@ -4,8 +4,21 @@ const wrapAsync = require('../helpers').wrapAsync; const router = express.Router(); const sjs = require('sequelize-json-schema'); const { getWidget, askGpt } = require('../services/openai'); -const RolesService = require('../services/roles'); -const RolesDBApi = require("../db/api/roles"); +const { LocalAIApi } = require('../ai/LocalAIApi'); + +const loadRolesModules = () => { + try { + return { + RolesService: require('../services/roles'), + RolesDBApi: require('../db/api/roles'), + }; + } catch (error) { + console.error('Roles modules are missing. Advanced roles are required for this endpoint.', error); + const err = new Error('Roles modules are missing. Advanced roles are required for this endpoint.'); + err.originalError = error; + throw err; + } +}; /** * @swagger @@ -59,6 +72,7 @@ const RolesDBApi = require("../db/api/roles"); router.delete( '/roles-info/:infoId', wrapAsync(async (req, res) => { + const { RolesService } = loadRolesModules(); const role = await RolesService.removeRoleInfoById( req.query.infoId, req.query.roleId, @@ -116,6 +130,7 @@ router.delete( router.get( '/info-by-key', wrapAsync(async (req, res) => { + const { RolesService, RolesDBApi } = loadRolesModules(); const roleId = req.query.roleId; const key = req.query.key; const currentUser = req.currentUser; @@ -124,16 +139,16 @@ router.get( roleId, currentUser, ); - const role = await RolesDBApi.findBy({ id: roleId }); + const role = await RolesDBApi.findBy({ id: roleId }); if (!role?.role_customization) { - await Promise.all(["pie","bar"].map(async (e)=>{ + await Promise.all(["pie", "bar"].map(async (e) => { const schema = await sjs.getSequelizeSchema(db.sequelize, {}); const payload = { description: `Create some cool ${e} chart`, modelDefinition: schema.definitions, }; const widgetId = await getWidget(payload, currentUser?.id, roleId); - if(widgetId){ + if (widgetId) { await RolesService.addRoleInfo( roleId, currentUser?.id, @@ -156,6 +171,7 @@ router.get( router.post( '/create_widget', wrapAsync(async (req, res) => { + const { RolesService } = loadRolesModules(); const { description, userId, roleId } = req.body; const currentUser = req.currentUser; @@ -185,13 +201,13 @@ router.post( /** * @swagger - * /api/openai/ask: + * /api/openai/response: * post: * security: * - bearerAuth: [] * tags: [OpenAI] - * summary: Ask a question to ChatGPT - * description: Send a question to OpenAI's ChatGPT and get a response + * summary: Proxy a Responses API request + * description: Sends the payload to the Flatlogic AI proxy and returns the response. * requestBody: * required: true * content: @@ -199,12 +215,73 @@ router.post( * schema: * type: object * properties: - * question: + * input: + * type: array + * description: List of messages with roles and content. + * items: + * type: object + * properties: + * role: + * type: string + * content: + * type: string + * options: + * type: object + * description: Optional polling controls. + * properties: + * poll_interval: + * type: number + * poll_timeout: + * type: number + * responses: + * 200: + * description: AI response received + * 400: + * description: Invalid request + * 401: + * $ref: "#/components/responses/UnauthorizedError" + * 502: + * description: Proxy error + */ +router.post( + '/response', + wrapAsync(async (req, res) => { + const body = req.body || {}; + const options = body.options || {}; + const payload = { ...body }; + delete payload.options; + + const response = await LocalAIApi.createResponse(payload, options); + + if (response.success) { + return res.status(200).send(response); + } + + console.error('AI proxy error:', response); + const status = response.error === 'input_missing' ? 400 : 502; + return res.status(status).send(response); + }), +); + +/** + * @swagger + * /api/openai/ask: + * post: + * security: + * - bearerAuth: [] + * tags: [OpenAI] + * summary: Ask a question to ChatGPT + * description: Send a question through the Flatlogic AI proxy and get a response + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * prompt: * type: string * description: The question to ask ChatGPT - * apiKey: - * type: string - * description: OpenAI API key * responses: * 200: * description: Question successfully answered @@ -233,7 +310,7 @@ router.post( if (!prompt) { return res.status(400).send({ success: false, - error: 'Question and API key are required', + error: 'Prompt is required', }); } diff --git a/backend/src/routes/reports.js b/backend/src/routes/reports.js deleted file mode 100644 index 299fa92..0000000 --- a/backend/src/routes/reports.js +++ /dev/null @@ -1,426 +0,0 @@ - -const express = require('express'); - -const ReportsService = require('../services/reports'); -const ReportsDBApi = require('../db/api/reports'); -const wrapAsync = require('../helpers').wrapAsync; - - -const router = express.Router(); - -const { parse } = require('json2csv'); - - -const { - checkCrudPermissions, -} = require('../middlewares/check-permissions'); - -router.use(checkCrudPermissions('reports')); - - -/** - * @swagger - * components: - * schemas: - * Reports: - * type: object - * properties: - - - - - */ - -/** - * @swagger - * tags: - * name: Reports - * description: The Reports managing API - */ - -/** -* @swagger -* /api/reports: -* post: -* security: -* - bearerAuth: [] -* tags: [Reports] -* summary: Add new item -* description: Add new item -* requestBody: -* required: true -* content: -* application/json: -* schema: -* properties: -* data: -* description: Data of the updated item -* type: object -* $ref: "#/components/schemas/Reports" -* responses: -* 200: -* description: The item was successfully added -* content: -* application/json: -* schema: -* $ref: "#/components/schemas/Reports" -* 401: -* $ref: "#/components/responses/UnauthorizedError" -* 405: -* description: Invalid input data -* 500: -* description: Some server error -*/ -router.post('/', wrapAsync(async (req, res) => { - const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; - const link = new URL(referer); - await ReportsService.create(req.body.data, req.currentUser, true, link.host); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/budgets/bulk-import: - * post: - * security: - * - bearerAuth: [] - * tags: [Reports] - * summary: Bulk import items - * description: Bulk import items - * requestBody: - * required: true - * content: - * application/json: - * schema: - * properties: - * data: - * description: Data of the updated items - * type: array - * items: - * $ref: "#/components/schemas/Reports" - * responses: - * 200: - * description: The items were successfully imported - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Reports" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 405: - * description: Invalid input data - * 500: - * description: Some server error - * - */ -router.post('/bulk-import', wrapAsync(async (req, res) => { - const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; - const link = new URL(referer); - await ReportsService.bulkImport(req, res, true, link.host); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/reports/{id}: - * put: - * security: - * - bearerAuth: [] - * tags: [Reports] - * summary: Update the data of the selected item - * description: Update the data of the selected item - * parameters: - * - in: path - * name: id - * description: Item ID to update - * required: true - * schema: - * type: string - * requestBody: - * description: Set new item data - * required: true - * content: - * application/json: - * schema: - * properties: - * id: - * description: ID of the updated item - * type: string - * data: - * description: Data of the updated item - * type: object - * $ref: "#/components/schemas/Reports" - * required: - * - id - * responses: - * 200: - * description: The item data was successfully updated - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Reports" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.put('/:id', wrapAsync(async (req, res) => { - await ReportsService.update(req.body.data, req.body.id, req.currentUser); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/reports/{id}: - * delete: - * security: - * - bearerAuth: [] - * tags: [Reports] - * summary: Delete the selected item - * description: Delete the selected item - * parameters: - * - in: path - * name: id - * description: Item ID to delete - * required: true - * schema: - * type: string - * responses: - * 200: - * description: The item was successfully deleted - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Reports" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.delete('/:id', wrapAsync(async (req, res) => { - await ReportsService.remove(req.params.id, req.currentUser); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/reports/deleteByIds: - * post: - * security: - * - bearerAuth: [] - * tags: [Reports] - * summary: Delete the selected item list - * description: Delete the selected item list - * requestBody: - * required: true - * content: - * application/json: - * schema: - * properties: - * ids: - * description: IDs of the updated items - * type: array - * responses: - * 200: - * description: The items was successfully deleted - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Reports" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Items not found - * 500: - * description: Some server error - */ -router.post('/deleteByIds', wrapAsync(async (req, res) => { - await ReportsService.deleteByIds(req.body.data, req.currentUser); - const payload = true; - res.status(200).send(payload); - })); - -/** - * @swagger - * /api/reports: - * get: - * security: - * - bearerAuth: [] - * tags: [Reports] - * summary: Get all reports - * description: Get all reports - * responses: - * 200: - * description: Reports list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Reports" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error -*/ -router.get('/', wrapAsync(async (req, res) => { - const filetype = req.query.filetype - - const currentUser = req.currentUser; - const payload = await ReportsDBApi.findAll( - req.query, { currentUser } - ); - if (filetype && filetype === 'csv') { - const fields = ['id', - - - - ]; - const opts = { fields }; - try { - const csv = parse(payload.rows, opts); - res.status(200).attachment(csv); - res.send(csv) - - } catch (err) { - console.error(err); - } - } else { - res.status(200).send(payload); - } - -})); - -/** - * @swagger - * /api/reports/count: - * get: - * security: - * - bearerAuth: [] - * tags: [Reports] - * summary: Count all reports - * description: Count all reports - * responses: - * 200: - * description: Reports count successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Reports" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/count', wrapAsync(async (req, res) => { - - const currentUser = req.currentUser; - const payload = await ReportsDBApi.findAll( - req.query, - null, - { countOnly: true, currentUser } - ); - - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/reports/autocomplete: - * get: - * security: - * - bearerAuth: [] - * tags: [Reports] - * summary: Find all reports that match search criteria - * description: Find all reports that match search criteria - * responses: - * 200: - * description: Reports list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Reports" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/autocomplete', async (req, res) => { - - const payload = await ReportsDBApi.findAllAutocomplete( - req.query.query, - req.query.limit, - req.query.offset, - - ); - - res.status(200).send(payload); -}); - -/** - * @swagger - * /api/reports/{id}: - * get: - * security: - * - bearerAuth: [] - * tags: [Reports] - * summary: Get selected item - * description: Get selected item - * parameters: - * - in: path - * name: id - * description: ID of item to get - * required: true - * schema: - * type: string - * responses: - * 200: - * description: Selected item successfully received - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Reports" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.get('/:id', wrapAsync(async (req, res) => { - const payload = await ReportsDBApi.findBy( - { id: req.params.id }, - ); - - - - res.status(200).send(payload); -})); - -router.use('/', require('../helpers').commonErrorHandler); - -module.exports = router; diff --git a/backend/src/routes/team_members.js b/backend/src/routes/team_memberships.js similarity index 76% rename from backend/src/routes/team_members.js rename to backend/src/routes/team_memberships.js index 1016d6c..a4109e3 100644 --- a/backend/src/routes/team_members.js +++ b/backend/src/routes/team_memberships.js @@ -1,8 +1,8 @@ const express = require('express'); -const Team_membersService = require('../services/team_members'); -const Team_membersDBApi = require('../db/api/team_members'); +const Team_membershipsService = require('../services/team_memberships'); +const Team_membershipsDBApi = require('../db/api/team_memberships'); const wrapAsync = require('../helpers').wrapAsync; @@ -15,14 +15,14 @@ const { checkCrudPermissions, } = require('../middlewares/check-permissions'); -router.use(checkCrudPermissions('team_members')); +router.use(checkCrudPermissions('team_memberships')); /** * @swagger * components: * schemas: - * Team_members: + * Team_memberships: * type: object * properties: @@ -34,17 +34,17 @@ router.use(checkCrudPermissions('team_members')); /** * @swagger * tags: - * name: Team_members - * description: The Team_members managing API + * name: Team_memberships + * description: The Team_memberships managing API */ /** * @swagger -* /api/team_members: +* /api/team_memberships: * post: * security: * - bearerAuth: [] -* tags: [Team_members] +* tags: [Team_memberships] * summary: Add new item * description: Add new item * requestBody: @@ -56,14 +56,14 @@ router.use(checkCrudPermissions('team_members')); * data: * description: Data of the updated item * type: object -* $ref: "#/components/schemas/Team_members" +* $ref: "#/components/schemas/Team_memberships" * responses: * 200: * description: The item was successfully added * content: * application/json: * schema: -* $ref: "#/components/schemas/Team_members" +* $ref: "#/components/schemas/Team_memberships" * 401: * $ref: "#/components/responses/UnauthorizedError" * 405: @@ -74,7 +74,7 @@ router.use(checkCrudPermissions('team_members')); router.post('/', wrapAsync(async (req, res) => { const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; const link = new URL(referer); - await Team_membersService.create(req.body.data, req.currentUser, true, link.host); + await Team_membershipsService.create(req.body.data, req.currentUser, true, link.host); const payload = true; res.status(200).send(payload); })); @@ -85,7 +85,7 @@ router.post('/', wrapAsync(async (req, res) => { * post: * security: * - bearerAuth: [] - * tags: [Team_members] + * tags: [Team_memberships] * summary: Bulk import items * description: Bulk import items * requestBody: @@ -98,14 +98,14 @@ router.post('/', wrapAsync(async (req, res) => { * description: Data of the updated items * type: array * items: - * $ref: "#/components/schemas/Team_members" + * $ref: "#/components/schemas/Team_memberships" * responses: * 200: * description: The items were successfully imported * content: * application/json: * schema: - * $ref: "#/components/schemas/Team_members" + * $ref: "#/components/schemas/Team_memberships" * 401: * $ref: "#/components/responses/UnauthorizedError" * 405: @@ -117,18 +117,18 @@ router.post('/', wrapAsync(async (req, res) => { router.post('/bulk-import', wrapAsync(async (req, res) => { const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; const link = new URL(referer); - await Team_membersService.bulkImport(req, res, true, link.host); + await Team_membershipsService.bulkImport(req, res, true, link.host); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/team_members/{id}: + * /api/team_memberships/{id}: * put: * security: * - bearerAuth: [] - * tags: [Team_members] + * tags: [Team_memberships] * summary: Update the data of the selected item * description: Update the data of the selected item * parameters: @@ -151,7 +151,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => { * data: * description: Data of the updated item * type: object - * $ref: "#/components/schemas/Team_members" + * $ref: "#/components/schemas/Team_memberships" * required: * - id * responses: @@ -160,7 +160,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Team_members" + * $ref: "#/components/schemas/Team_memberships" * 400: * description: Invalid ID supplied * 401: @@ -171,18 +171,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => { * description: Some server error */ router.put('/:id', wrapAsync(async (req, res) => { - await Team_membersService.update(req.body.data, req.body.id, req.currentUser); + await Team_membershipsService.update(req.body.data, req.body.id, req.currentUser); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/team_members/{id}: + * /api/team_memberships/{id}: * delete: * security: * - bearerAuth: [] - * tags: [Team_members] + * tags: [Team_memberships] * summary: Delete the selected item * description: Delete the selected item * parameters: @@ -198,7 +198,7 @@ router.put('/:id', wrapAsync(async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Team_members" + * $ref: "#/components/schemas/Team_memberships" * 400: * description: Invalid ID supplied * 401: @@ -209,18 +209,18 @@ router.put('/:id', wrapAsync(async (req, res) => { * description: Some server error */ router.delete('/:id', wrapAsync(async (req, res) => { - await Team_membersService.remove(req.params.id, req.currentUser); + await Team_membershipsService.remove(req.params.id, req.currentUser); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/team_members/deleteByIds: + * /api/team_memberships/deleteByIds: * post: * security: * - bearerAuth: [] - * tags: [Team_members] + * tags: [Team_memberships] * summary: Delete the selected item list * description: Delete the selected item list * requestBody: @@ -238,7 +238,7 @@ router.delete('/:id', wrapAsync(async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Team_members" + * $ref: "#/components/schemas/Team_memberships" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -247,29 +247,29 @@ router.delete('/:id', wrapAsync(async (req, res) => { * description: Some server error */ router.post('/deleteByIds', wrapAsync(async (req, res) => { - await Team_membersService.deleteByIds(req.body.data, req.currentUser); + await Team_membershipsService.deleteByIds(req.body.data, req.currentUser); const payload = true; res.status(200).send(payload); })); /** * @swagger - * /api/team_members: + * /api/team_memberships: * get: * security: * - bearerAuth: [] - * tags: [Team_members] - * summary: Get all team_members - * description: Get all team_members + * tags: [Team_memberships] + * summary: Get all team_memberships + * description: Get all team_memberships * responses: * 200: - * description: Team_members list successfully received + * description: Team_memberships list successfully received * content: * application/json: * schema: * type: array * items: - * $ref: "#/components/schemas/Team_members" + * $ref: "#/components/schemas/Team_memberships" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -281,7 +281,7 @@ router.get('/', wrapAsync(async (req, res) => { const filetype = req.query.filetype const currentUser = req.currentUser; - const payload = await Team_membersDBApi.findAll( + const payload = await Team_membershipsDBApi.findAll( req.query, { currentUser } ); if (filetype && filetype === 'csv') { @@ -307,22 +307,22 @@ router.get('/', wrapAsync(async (req, res) => { /** * @swagger - * /api/team_members/count: + * /api/team_memberships/count: * get: * security: * - bearerAuth: [] - * tags: [Team_members] - * summary: Count all team_members - * description: Count all team_members + * tags: [Team_memberships] + * summary: Count all team_memberships + * description: Count all team_memberships * responses: * 200: - * description: Team_members count successfully received + * description: Team_memberships count successfully received * content: * application/json: * schema: * type: array * items: - * $ref: "#/components/schemas/Team_members" + * $ref: "#/components/schemas/Team_memberships" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -333,7 +333,7 @@ router.get('/', wrapAsync(async (req, res) => { router.get('/count', wrapAsync(async (req, res) => { const currentUser = req.currentUser; - const payload = await Team_membersDBApi.findAll( + const payload = await Team_membershipsDBApi.findAll( req.query, null, { countOnly: true, currentUser } @@ -344,22 +344,22 @@ router.get('/count', wrapAsync(async (req, res) => { /** * @swagger - * /api/team_members/autocomplete: + * /api/team_memberships/autocomplete: * get: * security: * - bearerAuth: [] - * tags: [Team_members] - * summary: Find all team_members that match search criteria - * description: Find all team_members that match search criteria + * tags: [Team_memberships] + * summary: Find all team_memberships that match search criteria + * description: Find all team_memberships that match search criteria * responses: * 200: - * description: Team_members list successfully received + * description: Team_memberships list successfully received * content: * application/json: * schema: * type: array * items: - * $ref: "#/components/schemas/Team_members" + * $ref: "#/components/schemas/Team_memberships" * 401: * $ref: "#/components/responses/UnauthorizedError" * 404: @@ -369,7 +369,7 @@ router.get('/count', wrapAsync(async (req, res) => { */ router.get('/autocomplete', async (req, res) => { - const payload = await Team_membersDBApi.findAllAutocomplete( + const payload = await Team_membershipsDBApi.findAllAutocomplete( req.query.query, req.query.limit, req.query.offset, @@ -381,11 +381,11 @@ router.get('/autocomplete', async (req, res) => { /** * @swagger - * /api/team_members/{id}: + * /api/team_memberships/{id}: * get: * security: * - bearerAuth: [] - * tags: [Team_members] + * tags: [Team_memberships] * summary: Get selected item * description: Get selected item * parameters: @@ -401,7 +401,7 @@ router.get('/autocomplete', async (req, res) => { * content: * application/json: * schema: - * $ref: "#/components/schemas/Team_members" + * $ref: "#/components/schemas/Team_memberships" * 400: * description: Invalid ID supplied * 401: @@ -412,7 +412,7 @@ router.get('/autocomplete', async (req, res) => { * description: Some server error */ router.get('/:id', wrapAsync(async (req, res) => { - const payload = await Team_membersDBApi.findBy( + const payload = await Team_membershipsDBApi.findBy( { id: req.params.id }, ); diff --git a/backend/src/services/attachments.js b/backend/src/services/attachments.js deleted file mode 100644 index af8af9f..0000000 --- a/backend/src/services/attachments.js +++ /dev/null @@ -1,138 +0,0 @@ -const db = require('../db/models'); -const AttachmentsDBApi = require('../db/api/attachments'); -const processFile = require("../middlewares/upload"); -const ValidationError = require('./notifications/errors/validation'); -const csv = require('csv-parser'); -const axios = require('axios'); -const config = require('../config'); -const stream = require('stream'); - - - - - -module.exports = class AttachmentsService { - static async create(data, currentUser) { - const transaction = await db.sequelize.transaction(); - try { - await AttachmentsDBApi.create( - data, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - }; - - static async bulkImport(req, res, sendInvitationEmails = true, host) { - const transaction = await db.sequelize.transaction(); - - try { - await processFile(req, res); - const bufferStream = new stream.PassThrough(); - const results = []; - - await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream - - await new Promise((resolve, reject) => { - bufferStream - .pipe(csv()) - .on('data', (data) => results.push(data)) - .on('end', async () => { - console.log('CSV results', results); - resolve(); - }) - .on('error', (error) => reject(error)); - }) - - await AttachmentsDBApi.bulkImport(results, { - transaction, - ignoreDuplicates: true, - validate: true, - currentUser: req.currentUser - }); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - static async update(data, id, currentUser) { - const transaction = await db.sequelize.transaction(); - try { - let attachments = await AttachmentsDBApi.findBy( - {id}, - {transaction}, - ); - - if (!attachments) { - throw new ValidationError( - 'attachmentsNotFound', - ); - } - - const updatedAttachments = await AttachmentsDBApi.update( - id, - data, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - return updatedAttachments; - - } catch (error) { - await transaction.rollback(); - throw error; - } - }; - - static async deleteByIds(ids, currentUser) { - const transaction = await db.sequelize.transaction(); - - try { - await AttachmentsDBApi.deleteByIds(ids, { - currentUser, - transaction, - }); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - static async remove(id, currentUser) { - const transaction = await db.sequelize.transaction(); - - try { - await AttachmentsDBApi.remove( - id, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - -}; - - diff --git a/backend/src/services/notifications.js b/backend/src/services/notifications.js deleted file mode 100644 index 0aa0f9c..0000000 --- a/backend/src/services/notifications.js +++ /dev/null @@ -1,138 +0,0 @@ -const db = require('../db/models'); -const NotificationsDBApi = require('../db/api/notifications'); -const processFile = require("../middlewares/upload"); -const ValidationError = require('./notifications/errors/validation'); -const csv = require('csv-parser'); -const axios = require('axios'); -const config = require('../config'); -const stream = require('stream'); - - - - - -module.exports = class NotificationsService { - static async create(data, currentUser) { - const transaction = await db.sequelize.transaction(); - try { - await NotificationsDBApi.create( - data, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - }; - - static async bulkImport(req, res, sendInvitationEmails = true, host) { - const transaction = await db.sequelize.transaction(); - - try { - await processFile(req, res); - const bufferStream = new stream.PassThrough(); - const results = []; - - await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream - - await new Promise((resolve, reject) => { - bufferStream - .pipe(csv()) - .on('data', (data) => results.push(data)) - .on('end', async () => { - console.log('CSV results', results); - resolve(); - }) - .on('error', (error) => reject(error)); - }) - - await NotificationsDBApi.bulkImport(results, { - transaction, - ignoreDuplicates: true, - validate: true, - currentUser: req.currentUser - }); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - static async update(data, id, currentUser) { - const transaction = await db.sequelize.transaction(); - try { - let notifications = await NotificationsDBApi.findBy( - {id}, - {transaction}, - ); - - if (!notifications) { - throw new ValidationError( - 'notificationsNotFound', - ); - } - - const updatedNotifications = await NotificationsDBApi.update( - id, - data, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - return updatedNotifications; - - } catch (error) { - await transaction.rollback(); - throw error; - } - }; - - static async deleteByIds(ids, currentUser) { - const transaction = await db.sequelize.transaction(); - - try { - await NotificationsDBApi.deleteByIds(ids, { - currentUser, - transaction, - }); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - static async remove(id, currentUser) { - const transaction = await db.sequelize.transaction(); - - try { - await NotificationsDBApi.remove( - id, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - -}; - - diff --git a/backend/src/services/notifications/list.js b/backend/src/services/notifications/list.js index d21ba97..46a3ae0 100644 --- a/backend/src/services/notifications/list.js +++ b/backend/src/services/notifications/list.js @@ -1,6 +1,6 @@ const errors = { app: { - title: 'Team Projects Hub', + title: 'TeamFlow Manager', }, auth: { diff --git a/backend/src/services/openai.js b/backend/src/services/openai.js index 063bed5..3793398 100644 --- a/backend/src/services/openai.js +++ b/backend/src/services/openai.js @@ -1,13 +1,24 @@ const axios = require('axios'); -const { v4: uuid } = require('uuid'); -const RoleService = require('./roles'); const config = require('../config'); +const { LocalAIApi } = require('../ai/LocalAIApi'); + +const loadRoleService = () => { + try { + return require('./roles'); + } catch (error) { + console.error('Role service is missing. Advanced roles are required for this operation.', error); + const err = new Error('Role service is missing. Advanced roles are required for this operation.'); + err.originalError = error; + throw err; + } +}; module.exports = class OpenAiService { static async getWidget(payload, userId, roleId) { + const RoleService = loadRoleService(); const response = await axios.post( - `${config.flHost}/${config.project_uuid}/project_customization_widgets.json`, - payload, + `${config.flHost}/${config.project_uuid}/project_customization_widgets.json`, + payload, ); if (response.status >= 200 && response.status < 300) { @@ -21,47 +32,49 @@ module.exports = class OpenAiService { } static async askGpt(prompt) { - if (!config.gpt_key) { + if (!prompt) { return { success: false, - error: 'API key is required' + error: 'Prompt is required' }; } - try { - const response = await axios.post( - 'https://api.openai.com/v1/chat/completions', - { - model: 'gpt-4o', - messages: [ - { role: 'user', content: prompt } - ] - }, - { - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${config.gpt_key}` - } - } - ); - if (response.status >= 200 && response.status < 300) { - return { - success: true, - data: response.data.choices[0].message.content - }; - } else { - console.error('Error asking question to ChatGPT:', response.data); - return { - success: false, - error: response.data - }; + const response = await LocalAIApi.createResponse( + { + input: [{ role: 'user', content: prompt }], + }, + { + poll_interval: 5, + poll_timeout: 300, + }, + ); + + if (response.success) { + let text = LocalAIApi.extractText(response); + if (!text) { + try { + const decoded = LocalAIApi.decodeJsonFromResponse(response); + text = JSON.stringify(decoded); + } catch (error) { + console.error('AI JSON decode failed:', error); + return { + success: false, + error: 'AI response parsing failed', + details: error.message || String(error), + }; + } } - } catch (error) { - console.error('Error asking question to ChatGPT:', error.response?.data || error.message); return { - success: false, - error: error.response?.data || error.message + success: true, + data: text, }; } + + console.error('AI proxy error:', response); + return { + success: false, + error: response.error || response.message || 'AI proxy error', + response, + }; } }; diff --git a/backend/src/services/reports.js b/backend/src/services/reports.js deleted file mode 100644 index 4fcb050..0000000 --- a/backend/src/services/reports.js +++ /dev/null @@ -1,138 +0,0 @@ -const db = require('../db/models'); -const ReportsDBApi = require('../db/api/reports'); -const processFile = require("../middlewares/upload"); -const ValidationError = require('./notifications/errors/validation'); -const csv = require('csv-parser'); -const axios = require('axios'); -const config = require('../config'); -const stream = require('stream'); - - - - - -module.exports = class ReportsService { - static async create(data, currentUser) { - const transaction = await db.sequelize.transaction(); - try { - await ReportsDBApi.create( - data, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - }; - - static async bulkImport(req, res, sendInvitationEmails = true, host) { - const transaction = await db.sequelize.transaction(); - - try { - await processFile(req, res); - const bufferStream = new stream.PassThrough(); - const results = []; - - await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream - - await new Promise((resolve, reject) => { - bufferStream - .pipe(csv()) - .on('data', (data) => results.push(data)) - .on('end', async () => { - console.log('CSV results', results); - resolve(); - }) - .on('error', (error) => reject(error)); - }) - - await ReportsDBApi.bulkImport(results, { - transaction, - ignoreDuplicates: true, - validate: true, - currentUser: req.currentUser - }); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - static async update(data, id, currentUser) { - const transaction = await db.sequelize.transaction(); - try { - let reports = await ReportsDBApi.findBy( - {id}, - {transaction}, - ); - - if (!reports) { - throw new ValidationError( - 'reportsNotFound', - ); - } - - const updatedReports = await ReportsDBApi.update( - id, - data, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - return updatedReports; - - } catch (error) { - await transaction.rollback(); - throw error; - } - }; - - static async deleteByIds(ids, currentUser) { - const transaction = await db.sequelize.transaction(); - - try { - await ReportsDBApi.deleteByIds(ids, { - currentUser, - transaction, - }); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - static async remove(id, currentUser) { - const transaction = await db.sequelize.transaction(); - - try { - await ReportsDBApi.remove( - id, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - -}; - - diff --git a/backend/src/services/search.js b/backend/src/services/search.js index 44eafde..ce13465 100644 --- a/backend/src/services/search.js +++ b/backend/src/services/search.js @@ -78,26 +78,6 @@ module.exports = class SearchService { - - - - - - - - - - - - - - - - - - - - @@ -122,22 +102,6 @@ module.exports = class SearchService { - - - - - - - - - - - - - - - - diff --git a/backend/src/services/team_members.js b/backend/src/services/team_members.js deleted file mode 100644 index 3f35cca..0000000 --- a/backend/src/services/team_members.js +++ /dev/null @@ -1,138 +0,0 @@ -const db = require('../db/models'); -const Team_membersDBApi = require('../db/api/team_members'); -const processFile = require("../middlewares/upload"); -const ValidationError = require('./notifications/errors/validation'); -const csv = require('csv-parser'); -const axios = require('axios'); -const config = require('../config'); -const stream = require('stream'); - - - - - -module.exports = class Team_membersService { - static async create(data, currentUser) { - const transaction = await db.sequelize.transaction(); - try { - await Team_membersDBApi.create( - data, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - }; - - static async bulkImport(req, res, sendInvitationEmails = true, host) { - const transaction = await db.sequelize.transaction(); - - try { - await processFile(req, res); - const bufferStream = new stream.PassThrough(); - const results = []; - - await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream - - await new Promise((resolve, reject) => { - bufferStream - .pipe(csv()) - .on('data', (data) => results.push(data)) - .on('end', async () => { - console.log('CSV results', results); - resolve(); - }) - .on('error', (error) => reject(error)); - }) - - await Team_membersDBApi.bulkImport(results, { - transaction, - ignoreDuplicates: true, - validate: true, - currentUser: req.currentUser - }); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - static async update(data, id, currentUser) { - const transaction = await db.sequelize.transaction(); - try { - let team_members = await Team_membersDBApi.findBy( - {id}, - {transaction}, - ); - - if (!team_members) { - throw new ValidationError( - 'team_membersNotFound', - ); - } - - const updatedTeam_members = await Team_membersDBApi.update( - id, - data, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - return updatedTeam_members; - - } catch (error) { - await transaction.rollback(); - throw error; - } - }; - - static async deleteByIds(ids, currentUser) { - const transaction = await db.sequelize.transaction(); - - try { - await Team_membersDBApi.deleteByIds(ids, { - currentUser, - transaction, - }); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - static async remove(id, currentUser) { - const transaction = await db.sequelize.transaction(); - - try { - await Team_membersDBApi.remove( - id, - { - currentUser, - transaction, - }, - ); - - await transaction.commit(); - } catch (error) { - await transaction.rollback(); - throw error; - } - } - - -}; - - diff --git a/backend/src/services/comments.js b/backend/src/services/team_memberships.js similarity index 82% rename from backend/src/services/comments.js rename to backend/src/services/team_memberships.js index f570fed..54580f9 100644 --- a/backend/src/services/comments.js +++ b/backend/src/services/team_memberships.js @@ -1,5 +1,5 @@ const db = require('../db/models'); -const CommentsDBApi = require('../db/api/comments'); +const Team_membershipsDBApi = require('../db/api/team_memberships'); const processFile = require("../middlewares/upload"); const ValidationError = require('./notifications/errors/validation'); const csv = require('csv-parser'); @@ -11,11 +11,11 @@ const stream = require('stream'); -module.exports = class CommentsService { +module.exports = class Team_membershipsService { static async create(data, currentUser) { const transaction = await db.sequelize.transaction(); try { - await CommentsDBApi.create( + await Team_membershipsDBApi.create( data, { currentUser, @@ -51,7 +51,7 @@ module.exports = class CommentsService { .on('error', (error) => reject(error)); }) - await CommentsDBApi.bulkImport(results, { + await Team_membershipsDBApi.bulkImport(results, { transaction, ignoreDuplicates: true, validate: true, @@ -68,18 +68,18 @@ module.exports = class CommentsService { static async update(data, id, currentUser) { const transaction = await db.sequelize.transaction(); try { - let comments = await CommentsDBApi.findBy( + let team_memberships = await Team_membershipsDBApi.findBy( {id}, {transaction}, ); - if (!comments) { + if (!team_memberships) { throw new ValidationError( - 'commentsNotFound', + 'team_membershipsNotFound', ); } - const updatedComments = await CommentsDBApi.update( + const updatedTeam_memberships = await Team_membershipsDBApi.update( id, data, { @@ -89,7 +89,7 @@ module.exports = class CommentsService { ); await transaction.commit(); - return updatedComments; + return updatedTeam_memberships; } catch (error) { await transaction.rollback(); @@ -101,7 +101,7 @@ module.exports = class CommentsService { const transaction = await db.sequelize.transaction(); try { - await CommentsDBApi.deleteByIds(ids, { + await Team_membershipsDBApi.deleteByIds(ids, { currentUser, transaction, }); @@ -117,7 +117,7 @@ module.exports = class CommentsService { const transaction = await db.sequelize.transaction(); try { - await CommentsDBApi.remove( + await Team_membershipsDBApi.remove( id, { currentUser, diff --git a/backend/watcher.js b/backend/watcher.js index c9218d5..3796f8d 100644 --- a/backend/watcher.js +++ b/backend/watcher.js @@ -2,13 +2,16 @@ const chokidar = require('chokidar'); const { exec } = require('child_process'); const nodemon = require('nodemon'); +const nodeEnv = 'dev_stage'; +const childEnv = { ...process.env, NODE_ENV: nodeEnv }; + const migrationsWatcher = chokidar.watch('./src/db/migrations', { persistent: true, ignoreInitial: true }); migrationsWatcher.on('add', (filePath) => { console.log(`[DEBUG] New migration file: ${filePath}`); - exec('npm run db:migrate', (error, stdout, stderr) => { + exec('npm run db:migrate', { env: childEnv }, (error, stdout, stderr) => { console.log(stdout); if (error) { console.error(stderr); @@ -22,7 +25,7 @@ const seedersWatcher = chokidar.watch('./src/db/seeders', { }); seedersWatcher.on('add', (filePath) => { console.log(`[DEBUG] New seed file: ${filePath}`); - exec('npm run db:seed', (error, stdout, stderr) => { + exec('npm run db:seed', { env: childEnv }, (error, stdout, stderr) => { console.log(stdout); if (error) { console.error(stderr); @@ -32,6 +35,7 @@ seedersWatcher.on('add', (filePath) => { nodemon({ script: './src/index.js', + env: childEnv, ignore: ['./src/db/migrations', './src/db/seeders'], delay: '500' }); @@ -42,4 +46,4 @@ nodemon.on('start', () => { nodemon.on('restart', (files) => { console.log('Nodemon restarted due changes in:', files); -}); \ No newline at end of file +}); diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 634655e..8f7de8b 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -25,7 +25,7 @@ services: - ./data/db:/var/lib/postgresql/data environment: - POSTGRES_HOST_AUTH_METHOD=trust - - POSTGRES_DB=db_team_projects_hub + - POSTGRES_DB=db_teamflow_manager ports: - "5432:5432" logging: diff --git a/frontend/README.md b/frontend/README.md index 64359be..c42fd4d 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,4 +1,4 @@ -# Team Projects Hub +# TeamFlow Manager ## This project was generated by Flatlogic Platform. ## Install diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts index 00a8785..254b73c 100644 --- a/frontend/next-env.d.ts +++ b/frontend/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -/// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/frontend/src/colors.ts b/frontend/src/colors.ts index 71e116a..ca08bb9 100644 --- a/frontend/src/colors.ts +++ b/frontend/src/colors.ts @@ -1,42 +1,49 @@ import type { ColorButtonKey } from './interfaces' -export const gradientBgBase = 'bg-gradient-to-tr' -export const colorBgBase = "bg-violet-50/50" -export const gradientBgPurplePink = `${gradientBgBase} from-purple-400 via-pink-500 to-red-500` -export const gradientBgViolet = `${gradientBgBase} ${colorBgBase}` -export const gradientBgDark = `${gradientBgBase} from-dark-700 via-dark-900 to-dark-800`; -export const gradientBgPinkRed = `${gradientBgBase} from-pink-400 via-red-500 to-yellow-500` +// Base background color +export const colorBgBase = 'bg-gray-100 dark:bg-gray-900' +// No more gradients +export const gradientBgBase = '' +export const gradientBgPurplePink = '' +export const gradientBgViolet = '' +export const gradientBgDark = '' +export const gradientBgPinkRed = '' + +// Simplified background colors export const colorsBgLight = { - white: 'bg-white text-black', - light: ' bg-white text-black text-black dark:bg-dark-900 dark:text-white', + white: 'bg-white text-black dark:bg-gray-800 dark:text-white', + light: 'bg-gray-100 text-black dark:bg-gray-800 dark:text-white', contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black', - success: 'bg-emerald-500 border-emerald-500 dark:bg-pavitra-blue dark:border-pavitra-blue text-white', - danger: 'bg-red-500 border-red-500 text-white', + success: 'bg-green-500 border-green-500 text-white', + danger: 'bg-red-600 border-red-600 text-white', warning: 'bg-yellow-500 border-yellow-500 text-white', - info: 'bg-blue-500 border-blue-500 dark:bg-pavitra-blue dark:border-pavitra-blue text-white', + info: 'bg-blue-500 border-blue-500 text-white', } +// Simplified text colors export const colorsText = { - white: 'text-black dark:text-slate-100', - light: 'text-gray-700 dark:text-slate-400', + white: 'text-black dark:text-gray-100', + light: 'text-gray-700 dark:text-gray-400', contrast: 'dark:text-white', - success: 'text-emerald-500', - danger: 'text-red-500', + success: 'text-green-500', + danger: 'text-red-600', warning: 'text-yellow-500', info: 'text-blue-500', -}; +} +// Simplified outline colors export const colorsOutline = { - white: [colorsText.white, 'border-gray-100'].join(' '), - light: [colorsText.light, 'border-gray-100'].join(' '), - contrast: [colorsText.contrast, 'border-gray-900 dark:border-slate-100'].join(' '), - success: [colorsText.success, 'border-emerald-500'].join(' '), - danger: [colorsText.danger, 'border-red-500'].join(' '), + white: [colorsText.white, 'border-gray-200 dark:border-gray-700'].join(' '), + light: [colorsText.light, 'border-gray-200 dark:border-gray-700'].join(' '), + contrast: [colorsText.contrast, 'border-gray-700 dark:border-gray-200'].join(' '), + success: [colorsText.success, 'border-green-500'].join(' '), + danger: [colorsText.danger, 'border-red-600'].join(' '), warning: [colorsText.warning, 'border-yellow-500'].join(' '), info: [colorsText.info, 'border-blue-500'].join(' '), -}; +} +// Simplified button color logic export const getButtonColor = ( color: ColorButtonKey, isOutlined: boolean, @@ -47,92 +54,104 @@ export const getButtonColor = ( return '' } - const colors = { - ring: { - white: 'ring-gray-200 dark:ring-gray-500', - whiteDark: 'ring-gray-200 dark:ring-dark-500', - lightDark: 'ring-gray-200 dark:ring-gray-500', - contrast: 'ring-gray-300 dark:ring-gray-400', - success: 'ring-emerald-300 dark:ring-pavitra-blue', - danger: 'ring-red-300 dark:ring-red-700', - warning: 'ring-yellow-300 dark:ring-yellow-700', - info: "ring-blue-300 dark:ring-pavitra-blue", + const baseColors = { + white: { + bg: 'bg-white text-black', + border: 'border-gray-300', + hover: 'hover:bg-gray-50', + active: 'bg-gray-100', + ring: 'ring-gray-300', }, - active: { - white: 'bg-gray-100', - whiteDark: 'bg-gray-100 dark:bg-dark-800', - lightDark: 'bg-gray-200 dark:bg-slate-700', - contrast: 'bg-gray-700 dark:bg-slate-100', - success: 'bg-emerald-700 dark:bg-pavitra-blue', - danger: 'bg-red-700 dark:bg-red-600', - warning: 'bg-yellow-700 dark:bg-yellow-600', - info: 'bg-blue-700 dark:bg-pavitra-blue', + light: { + bg: 'bg-gray-200 text-black', + border: 'border-gray-300', + hover: 'hover:bg-gray-300', + active: 'bg-gray-300', + ring: 'ring-gray-300', }, - bg: { - white: 'bg-white text-black', - whiteDark: 'bg-white text-black dark:bg-dark-900 dark:text-white', - lightDark: 'bg-gray-100 text-black dark:bg-slate-800 dark:text-white', - contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black', - success: 'bg-emerald-600 dark:bg-pavitra-blue text-white', - danger: 'bg-red-600 text-white dark:bg-red-500 ', - warning: 'bg-yellow-600 dark:bg-yellow-500 text-white', - info: " bg-blue-600 dark:bg-pavitra-blue text-white ", + contrast: { + bg: 'bg-gray-800 text-white', + border: 'border-gray-800', + hover: 'hover:bg-gray-700', + active: 'bg-gray-700', + ring: 'ring-gray-300', }, - bgHover: { - white: 'hover:bg-gray-100', - whiteDark: 'hover:bg-gray-100 hover:dark:bg-dark-800', - lightDark: 'hover:bg-gray-200 hover:dark:bg-slate-700', - contrast: 'hover:bg-gray-700 hover:dark:bg-slate-100', - success: - 'hover:bg-emerald-700 hover:border-emerald-700 hover:dark:bg-pavitra-blue hover:dark:border-pavitra-blue', - danger: - 'hover:bg-red-700 hover:border-red-700 hover:dark:bg-red-600 hover:dark:border-red-600', - warning: - 'hover:bg-yellow-700 hover:border-yellow-700 hover:dark:bg-yellow-600 hover:dark:border-yellow-600', - info: "hover:bg-blue-700 hover:border-blue-700 hover:dark:bg-pavitra-blue/80 hover:dark:border-pavitra-blue/80", + success: { + bg: 'bg-green-500 text-white', + border: 'border-green-500', + hover: 'hover:bg-green-600', + active: 'bg-green-600', + ring: 'ring-green-300', }, - borders: { - white: 'border-white', - whiteDark: 'border-white dark:border-dark-900', - lightDark: 'border-gray-100 dark:border-slate-800', - contrast: 'border-gray-800 dark:border-white', - success: 'border-emerald-600 dark:border-pavitra-blue', - danger: 'border-red-600 dark:border-red-500', - warning: 'border-yellow-600 dark:border-yellow-500', - info: "border-blue-600 border-blue-600 dark:border-pavitra-blue", + danger: { + bg: 'bg-red-600 text-white', + border: 'border-red-600', + hover: 'hover:bg-red-700', + active: 'bg-red-700', + ring: 'ring-red-300', }, - text: { - contrast: 'dark:text-slate-100', - success: 'text-emerald-600 dark:text-pavitra-blue', - danger: 'text-red-600 dark:text-red-500', - warning: 'text-yellow-600 dark:text-yellow-500', - info: 'text-blue-600 dark:text-pavitra-blue', + warning: { + bg: 'bg-yellow-500 text-white', + border: 'border-yellow-500', + hover: 'hover:bg-yellow-600', + active: 'bg-yellow-600', + ring: 'ring-yellow-300', }, - outlineHover: { - contrast: - 'hover:bg-gray-800 hover:text-gray-100 hover:dark:bg-slate-100 hover:dark:text-black', - success: 'hover:bg-emerald-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue', - danger: - 'hover:bg-red-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-red-600', - warning: - 'hover:bg-yellow-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-yellow-600', - info: "hover:bg-blue-600 hover:bg-blue-600 hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue", + info: { + bg: 'bg-blue-500 text-white', + border: 'border-blue-500', + hover: 'hover:bg-blue-600', + active: 'bg-blue-600', + ring: 'ring-blue-300', }, + whiteDark: { // Kept for mapping, but simplified + bg: 'bg-white text-black', + border: 'border-gray-300', + hover: 'hover:bg-gray-50', + active: 'bg-gray-100', + ring: 'ring-gray-300', + }, + lightDark: { // Kept for mapping, but simplified + bg: 'bg-gray-200 text-black', + border: 'border-gray-300', + hover: 'hover:bg-gray-300', + active: 'bg-gray-300', + ring: 'ring-gray-300', + }, + }; + + const colorSet = baseColors[color] || baseColors.info; // Default to 'info' + + const base = [ + 'w-full', + 'inline-flex', + 'justify-center', + 'items-center', + 'whitespace-nowrap', + 'focus:outline-none', + 'transition-colors', + 'focus:ring', + 'duration-150', + 'rounded', + colorSet.ring, + ] + + if (isOutlined) { + base.push('bg-transparent', colorSet.border, `text-${color}-500`); + if (hasHover) { + base.push(`hover:bg-${color}-500`, 'hover:text-white'); + } + } else { + base.push(colorSet.bg, colorSet.border); + if (hasHover) { + base.push(colorSet.hover); + } } - const isOutlinedProcessed = isOutlined && ['white', 'whiteDark', 'lightDark'].indexOf(color) < 0 - - const base = [colors.borders[color], colors.ring[color]] - if (isActive) { - base.push(colors.active[color]) - } else { - base.push(isOutlinedProcessed ? colors.text[color] : colors.bg[color]) + base.push(colorSet.active); } - if (hasHover) { - base.push(isOutlinedProcessed ? colors.outlineHover[color] : colors.bgHover[color]) - } return base.join(' ') -} +} \ No newline at end of file diff --git a/frontend/src/components/AsideMenu.tsx b/frontend/src/components/AsideMenu.tsx index 442dfac..9be0413 100644 --- a/frontend/src/components/AsideMenu.tsx +++ b/frontend/src/components/AsideMenu.tsx @@ -19,7 +19,7 @@ export default function AsideMenu({ <> state.style.corners); - const asideStyle = useAppSelector((state) => state.style.asideStyle) - const asideBrandStyle = useAppSelector((state) => state.style.asideBrandStyle) - const asideScrollbarsStyle = useAppSelector((state) => state.style.asideScrollbarsStyle) const darkMode = useAppSelector((state) => state.style.darkMode) const handleAsideLgCloseClick = (e: React.MouseEvent) => { @@ -25,35 +19,23 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props props.onAsideLgCloseClick() } - return (