v1
This commit is contained in:
parent
3ff3cd3763
commit
95aef58de7
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,3 +1,6 @@
|
|||||||
|
/backend/node_modules
|
||||||
|
/frontend/node_modules
|
||||||
node_modules/
|
node_modules/
|
||||||
*/node_modules/
|
*/node_modules/
|
||||||
*/build/
|
**/node_modules/
|
||||||
|
*/build/
|
||||||
4
502.html
4
502.html
@ -129,8 +129,8 @@
|
|||||||
<p class="tip">The application is currently launching. The page will automatically refresh once site is
|
<p class="tip">The application is currently launching. The page will automatically refresh once site is
|
||||||
available.</p>
|
available.</p>
|
||||||
<div class="project-info">
|
<div class="project-info">
|
||||||
<h2>Team Projects Hub</h2>
|
<h2>TeamFlow Manager</h2>
|
||||||
<p>Team Projects Hub for collaborative project and task management with roles, deadlines, and reporting.</p>
|
<p>TeamFlow Manager: collaborative project and task management with roles, deadlines, calendars, and reporting.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="loader-container">
|
<div class="loader-container">
|
||||||
<img src="https://flatlogic.com/blog/wp-content/uploads/2025/05/logo-bot-1.png" alt="App Logo"
|
<img src="https://flatlogic.com/blog/wp-content/uploads/2025/05/logo-bot-1.png" alt="App Logo"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
# Team Projects Hub
|
# TeamFlow Manager
|
||||||
|
|
||||||
|
|
||||||
## This project was generated by [Flatlogic Platform](https://flatlogic.com).
|
## This project was generated by [Flatlogic Platform](https://flatlogic.com).
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1,11 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
admin_pass: "d02fa926",
|
admin_pass: "156fca4c",
|
||||||
admin_email: "admin@flatlogic.com",
|
admin_email: "admin@flatlogic.com",
|
||||||
schema_encryption_key: process.env.SCHEMA_ENCRYPTION_KEY || '',
|
schema_encryption_key: process.env.SCHEMA_ENCRYPTION_KEY || '',
|
||||||
|
|
||||||
project_uuid: 'd02fa926-6491-481e-a115-b27b3ff8dc07',
|
project_uuid: '156fca4c-57d0-4fe3-8a0f-94f5514e3457',
|
||||||
flHost: process.env.NODE_ENV === 'production' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects',
|
flHost: process.env.NODE_ENV === 'production' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects',
|
||||||
|
|
||||||
gitea_domain: process.env.GITEA_DOMAIN || 'gitea.flatlogic.app',
|
gitea_domain: process.env.GITEA_DOMAIN || 'gitea.flatlogic.app',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
DB_NAME=app_37270
|
DB_NAME=app_37292
|
||||||
DB_USER=app_37270
|
DB_USER=app_37292
|
||||||
DB_PASS=d02fa926-6491-481e-a115-b27b3ff8dc07
|
DB_PASS=156fca4c-57d0-4fe3-8a0f-94f5514e3457
|
||||||
DB_HOST=127.0.0.1
|
DB_HOST=127.0.0.1
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
#Team Projects Hub - template backend,
|
#TeamFlow Manager - template backend,
|
||||||
|
|
||||||
#### Run App on local machine:
|
#### Run App on local machine:
|
||||||
|
|
||||||
@ -30,10 +30,10 @@
|
|||||||
- `psql postgres -U admin`
|
- `psql postgres -U admin`
|
||||||
|
|
||||||
- Type this command to creating a new database.
|
- Type this command to creating a new database.
|
||||||
- `postgres=> 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`.
|
- 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`
|
- `postgres=> \q`
|
||||||
|
|
||||||
------------
|
------------
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "teamprojectshub",
|
"name": "teamflowmanager",
|
||||||
"description": "Team Projects Hub - template backend",
|
"description": "TeamFlow Manager - template backend",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run db:migrate && npm run db:seed && npm run watch",
|
"start": "npm run db:migrate && npm run db:seed && npm run watch",
|
||||||
"db:migrate": "sequelize-cli db:migrate",
|
"db:migrate": "sequelize-cli db:migrate",
|
||||||
|
|||||||
484
backend/src/ai/LocalAIApi.js
Normal file
484
backend/src/ai/LocalAIApi.js
Normal file
@ -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,
|
||||||
|
};
|
||||||
@ -11,15 +11,15 @@ const config = {
|
|||||||
bcrypt: {
|
bcrypt: {
|
||||||
saltRounds: 12
|
saltRounds: 12
|
||||||
},
|
},
|
||||||
admin_pass: "d02fa926",
|
admin_pass: "156fca4c",
|
||||||
user_pass: "b27b3ff8dc07",
|
user_pass: "94f5514e3457",
|
||||||
admin_email: "admin@flatlogic.com",
|
admin_email: "admin@flatlogic.com",
|
||||||
providers: {
|
providers: {
|
||||||
LOCAL: 'local',
|
LOCAL: 'local',
|
||||||
GOOGLE: 'google',
|
GOOGLE: 'google',
|
||||||
MICROSOFT: 'microsoft'
|
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: '',
|
remote: '',
|
||||||
port: process.env.NODE_ENV === "production" ? "" : "8080",
|
port: process.env.NODE_ENV === "production" ? "" : "8080",
|
||||||
hostUI: process.env.NODE_ENV === "production" ? "" : "http://localhost",
|
hostUI: process.env.NODE_ENV === "production" ? "" : "http://localhost",
|
||||||
@ -39,7 +39,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
uploadDir: os.tmpdir(),
|
uploadDir: os.tmpdir(),
|
||||||
email: {
|
email: {
|
||||||
from: 'Team Projects Hub <app@flatlogic.app>',
|
from: 'TeamFlow Manager <app@flatlogic.app>',
|
||||||
host: 'email-smtp.us-east-1.amazonaws.com',
|
host: 'email-smtp.us-east-1.amazonaws.com',
|
||||||
port: 587,
|
port: 587,
|
||||||
auth: {
|
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',
|
flHost: process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'dev_stage' ? 'https://flatlogic.com/projects' : 'http://localhost:3000/projects',
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -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,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -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,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -172,10 +172,6 @@ module.exports = class PermissionsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,11 @@ module.exports = class ProjectsDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
|
status: data.status
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
start_date: data.start_date
|
start_date: data.start_date
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
@ -41,18 +46,8 @@ module.exports = class ProjectsDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
status: data.status
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
budget: data.budget
|
budget: data.budget
|
||||||
||
|
||
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
progress: data.progress
|
|
||||||
||
|
|
||||||
null
|
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;
|
return projects;
|
||||||
}
|
}
|
||||||
@ -97,6 +102,11 @@ module.exports = class ProjectsDBApi {
|
|||||||
description: item.description
|
description: item.description
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
status: item.status
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
start_date: item.start_date
|
start_date: item.start_date
|
||||||
@ -107,21 +117,11 @@ module.exports = class ProjectsDBApi {
|
|||||||
end_date: item.end_date
|
end_date: item.end_date
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
|
||||||
|
|
||||||
status: item.status
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
,
|
||||||
|
|
||||||
budget: item.budget
|
budget: item.budget
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
|
||||||
|
|
||||||
progress: item.progress
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -135,6 +135,18 @@ module.exports = class ProjectsDBApi {
|
|||||||
|
|
||||||
// For each item created, replace relation files
|
// 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;
|
return projects;
|
||||||
}
|
}
|
||||||
@ -157,21 +169,18 @@ module.exports = class ProjectsDBApi {
|
|||||||
if (data.description !== undefined) updatePayload.description = data.description;
|
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.start_date !== undefined) updatePayload.start_date = data.start_date;
|
||||||
|
|
||||||
|
|
||||||
if (data.end_date !== undefined) updatePayload.end_date = data.end_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.budget !== undefined) updatePayload.budget = data.budget;
|
||||||
|
|
||||||
|
|
||||||
if (data.progress !== undefined) updatePayload.progress = data.progress;
|
|
||||||
|
|
||||||
|
|
||||||
updatePayload.updatedById = currentUser.id;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await projects.update(updatePayload, {transaction});
|
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;
|
return projects;
|
||||||
}
|
}
|
||||||
@ -280,14 +299,6 @@ module.exports = class ProjectsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.reports_project = await projects.getReports_project({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.owner = await projects.getOwner({
|
output.owner = await projects.getOwner({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
@ -298,6 +309,11 @@ module.exports = class ProjectsDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
output.attachments = await projects.getAttachments({
|
||||||
|
transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -359,6 +375,11 @@ module.exports = class ProjectsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
model: db.file,
|
||||||
|
as: 'attachments',
|
||||||
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (filter) {
|
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) {
|
if (filter.active !== undefined) {
|
||||||
where = {
|
where = {
|
||||||
|
|||||||
@ -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,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -197,10 +197,6 @@ module.exports = class RolesDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.permissions = await roles.getPermissions({
|
output.permissions = await roles.getPermissions({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|||||||
@ -41,21 +41,26 @@ module.exports = class TasksDBApi {
|
|||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
due_date: data.due_date
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
start_date: data.start_date
|
start_date: data.start_date
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
|
due_date: data.due_date
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
estimated_hours: data.estimated_hours
|
estimated_hours: data.estimated_hours
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
|
spent_hours: data.spent_hours
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
completed: data.completed
|
completed: data.completed
|
||||||
||
|
||
|
||||||
false
|
false
|
||||||
@ -70,6 +75,10 @@ module.exports = class TasksDBApi {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
await tasks.setProject( data.project || null, {
|
||||||
|
transaction,
|
||||||
|
});
|
||||||
|
|
||||||
await tasks.setAssignee( data.assignee || null, {
|
await tasks.setAssignee( data.assignee || null, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
@ -78,14 +87,20 @@ module.exports = class TasksDBApi {
|
|||||||
transaction,
|
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;
|
return tasks;
|
||||||
}
|
}
|
||||||
@ -117,21 +132,26 @@ module.exports = class TasksDBApi {
|
|||||||
priority: item.priority
|
priority: item.priority
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
|
||||||
|
|
||||||
due_date: item.due_date
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
,
|
||||||
|
|
||||||
start_date: item.start_date
|
start_date: item.start_date
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
due_date: item.due_date
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
estimated_hours: item.estimated_hours
|
estimated_hours: item.estimated_hours
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
|
,
|
||||||
|
|
||||||
|
spent_hours: item.spent_hours
|
||||||
|
||
|
||||||
|
null
|
||||||
,
|
,
|
||||||
|
|
||||||
completed: item.completed
|
completed: item.completed
|
||||||
@ -151,6 +171,18 @@ module.exports = class TasksDBApi {
|
|||||||
|
|
||||||
// For each item created, replace relation files
|
// 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;
|
return tasks;
|
||||||
}
|
}
|
||||||
@ -179,15 +211,18 @@ module.exports = class TasksDBApi {
|
|||||||
if (data.priority !== undefined) updatePayload.priority = data.priority;
|
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.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.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;
|
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) {
|
if (data.assignee !== undefined) {
|
||||||
await tasks.setAssignee(
|
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;
|
return tasks;
|
||||||
}
|
}
|
||||||
@ -303,23 +348,12 @@ module.exports = class TasksDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.comments_task = await tasks.getComments_task({
|
|
||||||
|
output.project = await tasks.getProject({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
output.attachments_task = await tasks.getAttachments_task({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.notifications_related_task = await tasks.getNotifications_related_task({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.assignee = await tasks.getAssignee({
|
output.assignee = await tasks.getAssignee({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
@ -330,7 +364,7 @@ module.exports = class TasksDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
output.project = await tasks.getProject({
|
output.attachments = await tasks.getAttachments({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -360,6 +394,23 @@ module.exports = class TasksDBApi {
|
|||||||
|
|
||||||
let include = [
|
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,
|
model: db.users,
|
||||||
as: 'assignee',
|
as: 'assignee',
|
||||||
@ -394,25 +445,13 @@ module.exports = class TasksDBApi {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
model: db.projects,
|
model: db.file,
|
||||||
as: 'project',
|
as: 'attachments',
|
||||||
|
|
||||||
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}%` }))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
} : {},
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (filter) {
|
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) {
|
if (filter.start_dateRange) {
|
||||||
const [start, end] = 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) {
|
if (filter.estimated_hoursRange) {
|
||||||
const [start, end] = 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) {
|
if (filter.active !== undefined) {
|
||||||
where = {
|
where = {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const Utils = require('../utils');
|
|||||||
const Sequelize = db.Sequelize;
|
const Sequelize = db.Sequelize;
|
||||||
const Op = Sequelize.Op;
|
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 currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
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,
|
id: data.id || undefined,
|
||||||
|
|
||||||
|
member_label: data.member_label
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
role: data.role
|
role: data.role
|
||||||
||
|
||
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
joined_on: data.joined_on
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
display_label: data.display_label
|
|
||||||
||
|
|
||||||
null
|
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,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await team_members.setUser( data.user || null, {
|
await team_memberships.setUser( data.user || null, {
|
||||||
transaction,
|
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;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
// Prepare data - wrapping individual data transformations in a map() method
|
// 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,
|
id: item.id || undefined,
|
||||||
|
|
||||||
|
member_label: item.member_label
|
||||||
|
||
|
||||||
|
null
|
||||||
|
,
|
||||||
|
|
||||||
role: item.role
|
role: item.role
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
|
||||||
|
|
||||||
joined_on: item.joined_on
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
display_label: item.display_label
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -91,12 +81,12 @@ module.exports = class Team_membersDBApi {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Bulk create items
|
// 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
|
// For each item created, replace relation files
|
||||||
|
|
||||||
|
|
||||||
return team_members;
|
return team_memberships;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(id, data, options) {
|
static async update(id, data, options) {
|
||||||
@ -104,30 +94,27 @@ module.exports = class Team_membersDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
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 = {};
|
const updatePayload = {};
|
||||||
|
|
||||||
|
if (data.member_label !== undefined) updatePayload.member_label = data.member_label;
|
||||||
|
|
||||||
|
|
||||||
if (data.role !== undefined) updatePayload.role = data.role;
|
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;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await team_members.update(updatePayload, {transaction});
|
await team_memberships.update(updatePayload, {transaction});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (data.team !== undefined) {
|
if (data.team !== undefined) {
|
||||||
await team_members.setTeam(
|
await team_memberships.setTeam(
|
||||||
|
|
||||||
data.team,
|
data.team,
|
||||||
|
|
||||||
@ -136,7 +123,7 @@ module.exports = class Team_membersDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.user !== undefined) {
|
if (data.user !== undefined) {
|
||||||
await team_members.setUser(
|
await team_memberships.setUser(
|
||||||
|
|
||||||
data.user,
|
data.user,
|
||||||
|
|
||||||
@ -150,14 +137,14 @@ module.exports = class Team_membersDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
return team_members;
|
return team_memberships;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async deleteByIds(ids, options) {
|
static async deleteByIds(ids, options) {
|
||||||
const currentUser = (options && options.currentUser) || { id: null };
|
const currentUser = (options && options.currentUser) || { id: null };
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const team_members = await db.team_members.findAll({
|
const team_memberships = await db.team_memberships.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
@ -167,53 +154,53 @@ module.exports = class Team_membersDBApi {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await db.sequelize.transaction(async (transaction) => {
|
await db.sequelize.transaction(async (transaction) => {
|
||||||
for (const record of team_members) {
|
for (const record of team_memberships) {
|
||||||
await record.update(
|
await record.update(
|
||||||
{deletedBy: currentUser.id},
|
{deletedBy: currentUser.id},
|
||||||
{transaction}
|
{transaction}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const record of team_members) {
|
for (const record of team_memberships) {
|
||||||
await record.destroy({transaction});
|
await record.destroy({transaction});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return team_members;
|
return team_memberships;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async remove(id, options) {
|
static async remove(id, options) {
|
||||||
const currentUser = (options && options.currentUser) || {id: null};
|
const currentUser = (options && options.currentUser) || {id: null};
|
||||||
const transaction = (options && options.transaction) || undefined;
|
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
|
deletedBy: currentUser.id
|
||||||
}, {
|
}, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await team_members.destroy({
|
await team_memberships.destroy({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
return team_members;
|
return team_memberships;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async findBy(where, options) {
|
static async findBy(where, options) {
|
||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
const team_members = await db.team_members.findOne(
|
const team_memberships = await db.team_memberships.findOne(
|
||||||
{ where },
|
{ where },
|
||||||
{ transaction },
|
{ transaction },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!team_members) {
|
if (!team_memberships) {
|
||||||
return team_members;
|
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_memberships.getTeam({
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.team = await team_members.getTeam({
|
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
output.user = await team_members.getUser({
|
output.user = await team_memberships.getUser({
|
||||||
transaction
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -310,13 +293,13 @@ module.exports = class Team_membersDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.display_label) {
|
if (filter.member_label) {
|
||||||
where = {
|
where = {
|
||||||
...where,
|
...where,
|
||||||
[Op.and]: Utils.ilike(
|
[Op.and]: Utils.ilike(
|
||||||
'team_members',
|
'team_memberships',
|
||||||
'display_label',
|
'member_label',
|
||||||
filter.display_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) {
|
if (filter.active !== undefined) {
|
||||||
where = {
|
where = {
|
||||||
@ -419,7 +378,7 @@ module.exports = class Team_membersDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { rows, count } = await db.team_members.findAndCountAll(queryOptions);
|
const { rows, count } = await db.team_memberships.findAndCountAll(queryOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: options?.countOnly ? [] : rows,
|
rows: options?.countOnly ? [] : rows,
|
||||||
@ -441,25 +400,25 @@ module.exports = class Team_membersDBApi {
|
|||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ ['id']: Utils.uuid(query) },
|
{ ['id']: Utils.uuid(query) },
|
||||||
Utils.ilike(
|
Utils.ilike(
|
||||||
'team_members',
|
'team_memberships',
|
||||||
'display_label',
|
'member_label',
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const records = await db.team_members.findAll({
|
const records = await db.team_memberships.findAll({
|
||||||
attributes: [ 'id', 'display_label' ],
|
attributes: [ 'id', 'member_label' ],
|
||||||
where,
|
where,
|
||||||
limit: limit ? Number(limit) : undefined,
|
limit: limit ? Number(limit) : undefined,
|
||||||
offset: offset ? Number(offset) : undefined,
|
offset: offset ? Number(offset) : undefined,
|
||||||
orderBy: [['display_label', 'ASC']],
|
orderBy: [['member_label', 'ASC']],
|
||||||
});
|
});
|
||||||
|
|
||||||
return records.map((record) => ({
|
return records.map((record) => ({
|
||||||
id: record.id,
|
id: record.id,
|
||||||
label: record.display_label,
|
label: record.member_label,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,16 +28,6 @@ module.exports = class TeamsDBApi {
|
|||||||
|
|
||||||
description: data.description
|
description: data.description
|
||||||
||
|
||
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
visibility: data.visibility
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
members_count: data.members_count
|
|
||||||
||
|
|
||||||
null
|
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;
|
return teams;
|
||||||
}
|
}
|
||||||
@ -88,16 +64,6 @@ module.exports = class TeamsDBApi {
|
|||||||
description: item.description
|
description: item.description
|
||||||
||
|
||
|
||||||
null
|
null
|
||||||
,
|
|
||||||
|
|
||||||
visibility: item.visibility
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
|
||||||
|
|
||||||
members_count: item.members_count
|
|
||||||
||
|
|
||||||
null
|
|
||||||
,
|
,
|
||||||
|
|
||||||
importHash: item.importHash || null,
|
importHash: item.importHash || null,
|
||||||
@ -111,18 +77,6 @@ module.exports = class TeamsDBApi {
|
|||||||
|
|
||||||
// For each item created, replace relation files
|
// 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;
|
return teams;
|
||||||
}
|
}
|
||||||
@ -145,42 +99,17 @@ module.exports = class TeamsDBApi {
|
|||||||
if (data.description !== undefined) updatePayload.description = data.description;
|
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;
|
updatePayload.updatedById = currentUser.id;
|
||||||
|
|
||||||
await teams.update(updatePayload, {transaction});
|
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;
|
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
|
transaction
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -264,20 +193,6 @@ module.exports = class TeamsDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.avatar = await teams.getAvatar({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
output.owner = await teams.getOwner({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -303,30 +218,8 @@ module.exports = class TeamsDBApi {
|
|||||||
|
|
||||||
let include = [
|
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) {
|
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) {
|
if (filter.active !== undefined) {
|
||||||
where = {
|
where = {
|
||||||
@ -398,17 +267,8 @@ module.exports = class TeamsDBApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (filter.visibility) {
|
|
||||||
where = {
|
|
||||||
...where,
|
|
||||||
visibility: filter.visibility,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (filter.createdAtRange) {
|
if (filter.createdAtRange) {
|
||||||
|
|||||||
@ -403,12 +403,8 @@ module.exports = class UsersDBApi {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
output.teams_owner = await users.getTeams_owner({
|
|
||||||
transaction
|
|
||||||
});
|
|
||||||
|
|
||||||
|
output.team_memberships_user = await users.getTeam_memberships_user({
|
||||||
output.team_members_user = await users.getTeam_members_user({
|
|
||||||
transaction
|
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({
|
output.avatar = await users.getAvatar({
|
||||||
transaction
|
transaction
|
||||||
|
|||||||
@ -15,7 +15,7 @@ module.exports = {
|
|||||||
username: 'postgres',
|
username: 'postgres',
|
||||||
dialect: 'postgres',
|
dialect: 'postgres',
|
||||||
password: '',
|
password: '',
|
||||||
database: 'db_team_projects_hub',
|
database: 'db_teamflow_manager',
|
||||||
host: process.env.DB_HOST || 'localhost',
|
host: process.env.DB_HOST || 'localhost',
|
||||||
logging: console.log,
|
logging: console.log,
|
||||||
seederStorage: 'sequelize',
|
seederStorage: 'sequelize',
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -48,10 +48,6 @@ name: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,31 @@ description: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
status: {
|
||||||
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
values: [
|
||||||
|
|
||||||
|
"Draft",
|
||||||
|
|
||||||
|
|
||||||
|
"Active",
|
||||||
|
|
||||||
|
|
||||||
|
"OnHold",
|
||||||
|
|
||||||
|
|
||||||
|
"Completed",
|
||||||
|
|
||||||
|
|
||||||
|
"Cancelled"
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
start_date: {
|
start_date: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
@ -42,43 +67,11 @@ end_date: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
status: {
|
|
||||||
type: DataTypes.ENUM,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
values: [
|
|
||||||
|
|
||||||
"Planned",
|
|
||||||
|
|
||||||
|
|
||||||
"Active",
|
|
||||||
|
|
||||||
|
|
||||||
"OnHold",
|
|
||||||
|
|
||||||
|
|
||||||
"Completed",
|
|
||||||
|
|
||||||
|
|
||||||
"Archived"
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
budget: {
|
budget: {
|
||||||
type: DataTypes.DECIMAL,
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
progress: {
|
|
||||||
type: DataTypes.INTEGER,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
importHash: {
|
importHash: {
|
||||||
@ -116,18 +109,6 @@ progress: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.projects.hasMany(db.reports, {
|
|
||||||
as: 'reports_project',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'projectId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//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, {
|
db.projects.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
|
|||||||
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -81,10 +81,6 @@ role_customization: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -35,9 +35,6 @@ status: {
|
|||||||
|
|
||||||
values: [
|
values: [
|
||||||
|
|
||||||
"Backlog",
|
|
||||||
|
|
||||||
|
|
||||||
"ToDo",
|
"ToDo",
|
||||||
|
|
||||||
|
|
||||||
@ -47,10 +44,10 @@ status: {
|
|||||||
"InReview",
|
"InReview",
|
||||||
|
|
||||||
|
|
||||||
"Done",
|
"Blocked",
|
||||||
|
|
||||||
|
|
||||||
"Blocked"
|
"Done"
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -78,18 +75,18 @@ priority: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
due_date: {
|
|
||||||
type: DataTypes.DATE,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
start_date: {
|
start_date: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
due_date: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
estimated_hours: {
|
estimated_hours: {
|
||||||
@ -97,6 +94,13 @@ estimated_hours: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
spent_hours: {
|
||||||
|
type: DataTypes.DECIMAL,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
completed: {
|
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
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.tasks.belongsTo(db.projects, {
|
||||||
|
as: 'project',
|
||||||
|
foreignKey: {
|
||||||
|
name: 'projectId',
|
||||||
|
},
|
||||||
|
constraints: false,
|
||||||
|
});
|
||||||
|
|
||||||
db.tasks.belongsTo(db.users, {
|
db.tasks.belongsTo(db.users, {
|
||||||
as: 'assignee',
|
as: 'assignee',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
@ -184,17 +168,19 @@ completed: {
|
|||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
db.tasks.belongsTo(db.projects, {
|
|
||||||
as: 'project',
|
|
||||||
foreignKey: {
|
db.tasks.hasMany(db.file, {
|
||||||
name: 'projectId',
|
as: 'attachments',
|
||||||
},
|
foreignKey: 'belongsToId',
|
||||||
constraints: false,
|
constraints: false,
|
||||||
|
scope: {
|
||||||
|
belongsTo: db.tasks.getTableName(),
|
||||||
|
belongsToColumn: 'attachments',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.tasks.belongsTo(db.users, {
|
db.tasks.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,8 +5,8 @@ const bcrypt = require('bcrypt');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
|
||||||
module.exports = function(sequelize, DataTypes) {
|
module.exports = function(sequelize, DataTypes) {
|
||||||
const team_members = sequelize.define(
|
const team_memberships = sequelize.define(
|
||||||
'team_members',
|
'team_memberships',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
@ -14,6 +14,13 @@ module.exports = function(sequelize, DataTypes) {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
member_label: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
role: {
|
role: {
|
||||||
type: DataTypes.ENUM,
|
type: DataTypes.ENUM,
|
||||||
|
|
||||||
@ -30,20 +37,6 @@ role: {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
joined_on: {
|
|
||||||
type: DataTypes.DATE,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
display_label: {
|
|
||||||
type: DataTypes.TEXT,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
importHash: {
|
importHash: {
|
||||||
type: DataTypes.STRING(255),
|
type: DataTypes.STRING(255),
|
||||||
allowNull: true,
|
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
|
/// 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
|
//end loop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.team_members.belongsTo(db.teams, {
|
db.team_memberships.belongsTo(db.teams, {
|
||||||
as: 'team',
|
as: 'team',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'teamId',
|
name: 'teamId',
|
||||||
@ -87,7 +76,7 @@ display_label: {
|
|||||||
constraints: false,
|
constraints: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
db.team_members.belongsTo(db.users, {
|
db.team_memberships.belongsTo(db.users, {
|
||||||
as: 'user',
|
as: 'user',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'userId',
|
name: 'userId',
|
||||||
@ -98,18 +87,18 @@ display_label: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.team_members.belongsTo(db.users, {
|
db.team_memberships.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|
||||||
db.team_members.belongsTo(db.users, {
|
db.team_memberships.belongsTo(db.users, {
|
||||||
as: 'updatedBy',
|
as: 'updatedBy',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return team_members;
|
return team_memberships;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -26,29 +26,6 @@ description: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
visibility: {
|
|
||||||
type: DataTypes.ENUM,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
values: [
|
|
||||||
|
|
||||||
"Private",
|
|
||||||
|
|
||||||
|
|
||||||
"Public"
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
members_count: {
|
|
||||||
type: DataTypes.INTEGER,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
importHash: {
|
importHash: {
|
||||||
@ -74,8 +51,8 @@ members_count: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.teams.hasMany(db.team_members, {
|
db.teams.hasMany(db.team_memberships, {
|
||||||
as: 'team_members_team',
|
as: 'team_memberships_team',
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'teamId',
|
name: 'teamId',
|
||||||
},
|
},
|
||||||
@ -94,35 +71,13 @@ members_count: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//end loop
|
//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, {
|
db.teams.belongsTo(db.users, {
|
||||||
as: 'createdBy',
|
as: 'createdBy',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -144,17 +144,9 @@ provider: {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
db.users.hasMany(db.teams, {
|
|
||||||
as: 'teams_owner',
|
|
||||||
foreignKey: {
|
|
||||||
name: 'ownerId',
|
|
||||||
},
|
|
||||||
constraints: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
db.users.hasMany(db.team_memberships, {
|
||||||
db.users.hasMany(db.team_members, {
|
as: 'team_memberships_user',
|
||||||
as: 'team_members_user',
|
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
name: 'userId',
|
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
|
//end loop
|
||||||
|
|
||||||
|
|||||||
@ -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 },
|
{ id: getId("Contributor"), name: "Contributor", createdAt, updatedAt },
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const entities = [
|
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", entities.flatMap(createPermissions));
|
||||||
await queryInterface.bulkInsert("permissions", [{ id: getId(`READ_API_DOCS`), createdAt, updatedAt, name: `READ_API_DOCS` }]);
|
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("PlatformLead"), 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') },
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -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("PlatformLead"), permissionId: getId('DELETE_TEAM_MEMBERSHIPS') },
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{ 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') },
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -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("TeamManager"), permissionId: getId('UPDATE_PROJECTS') },
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{ createdAt, updatedAt, roles_permissionsId: getId("TeamLead"), 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') },
|
{ 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('UPDATE_TEAMS') },
|
||||||
{ createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_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('CREATE_TEAM_MEMBERSHIPS') },
|
||||||
{ createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_TEAM_MEMBERS') },
|
{ createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_TEAM_MEMBERSHIPS') },
|
||||||
{ createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_TEAM_MEMBERS') },
|
{ createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('UPDATE_TEAM_MEMBERSHIPS') },
|
||||||
{ createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_TEAM_MEMBERS') },
|
{ 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('CREATE_PROJECTS') },
|
||||||
{ createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('READ_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('UPDATE_TASKS') },
|
||||||
{ createdAt, updatedAt, roles_permissionsId: getId("Administrator"), permissionId: getId('DELETE_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') },
|
{ 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("PlatformLead")}' 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("ProjectLead")}' WHERE "email"='john@doe.com'`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -16,12 +16,10 @@ const fileRoutes = require('./routes/file');
|
|||||||
const searchRoutes = require('./routes/search');
|
const searchRoutes = require('./routes/search');
|
||||||
const pexelsRoutes = require('./routes/pexels');
|
const pexelsRoutes = require('./routes/pexels');
|
||||||
|
|
||||||
|
|
||||||
const openaiRoutes = require('./routes/openai');
|
const openaiRoutes = require('./routes/openai');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const usersRoutes = require('./routes/users');
|
const usersRoutes = require('./routes/users');
|
||||||
|
|
||||||
const rolesRoutes = require('./routes/roles');
|
const rolesRoutes = require('./routes/roles');
|
||||||
@ -30,20 +28,12 @@ const permissionsRoutes = require('./routes/permissions');
|
|||||||
|
|
||||||
const teamsRoutes = require('./routes/teams');
|
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 projectsRoutes = require('./routes/projects');
|
||||||
|
|
||||||
const tasksRoutes = require('./routes/tasks');
|
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) => {
|
const getBaseUrl = (url) => {
|
||||||
if (!url) return '';
|
if (!url) return '';
|
||||||
@ -55,8 +45,8 @@ const options = {
|
|||||||
openapi: "3.0.0",
|
openapi: "3.0.0",
|
||||||
info: {
|
info: {
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
title: "Team Projects Hub",
|
title: "TeamFlow Manager",
|
||||||
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.",
|
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: [
|
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/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/projects', passport.authenticate('jwt', {session: false}), projectsRoutes);
|
||||||
|
|
||||||
app.use('/api/tasks', passport.authenticate('jwt', {session: false}), tasksRoutes);
|
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(
|
app.use(
|
||||||
'/api/openai',
|
'/api/openai',
|
||||||
passport.authenticate('jwt', { session: false }),
|
passport.authenticate('jwt', { session: false }),
|
||||||
openaiRoutes,
|
openaiRoutes,
|
||||||
);
|
);
|
||||||
|
app.use(
|
||||||
|
'/api/ai',
|
||||||
|
passport.authenticate('jwt', { session: false }),
|
||||||
|
openaiRoutes,
|
||||||
|
);
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
'/api/search',
|
'/api/search',
|
||||||
|
|||||||
@ -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;
|
|
||||||
@ -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;
|
|
||||||
@ -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;
|
|
||||||
@ -4,8 +4,21 @@ const wrapAsync = require('../helpers').wrapAsync;
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const sjs = require('sequelize-json-schema');
|
const sjs = require('sequelize-json-schema');
|
||||||
const { getWidget, askGpt } = require('../services/openai');
|
const { getWidget, askGpt } = require('../services/openai');
|
||||||
const RolesService = require('../services/roles');
|
const { LocalAIApi } = require('../ai/LocalAIApi');
|
||||||
const RolesDBApi = require("../db/api/roles");
|
|
||||||
|
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
|
* @swagger
|
||||||
@ -59,6 +72,7 @@ const RolesDBApi = require("../db/api/roles");
|
|||||||
router.delete(
|
router.delete(
|
||||||
'/roles-info/:infoId',
|
'/roles-info/:infoId',
|
||||||
wrapAsync(async (req, res) => {
|
wrapAsync(async (req, res) => {
|
||||||
|
const { RolesService } = loadRolesModules();
|
||||||
const role = await RolesService.removeRoleInfoById(
|
const role = await RolesService.removeRoleInfoById(
|
||||||
req.query.infoId,
|
req.query.infoId,
|
||||||
req.query.roleId,
|
req.query.roleId,
|
||||||
@ -116,6 +130,7 @@ router.delete(
|
|||||||
router.get(
|
router.get(
|
||||||
'/info-by-key',
|
'/info-by-key',
|
||||||
wrapAsync(async (req, res) => {
|
wrapAsync(async (req, res) => {
|
||||||
|
const { RolesService, RolesDBApi } = loadRolesModules();
|
||||||
const roleId = req.query.roleId;
|
const roleId = req.query.roleId;
|
||||||
const key = req.query.key;
|
const key = req.query.key;
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
@ -124,16 +139,16 @@ router.get(
|
|||||||
roleId,
|
roleId,
|
||||||
currentUser,
|
currentUser,
|
||||||
);
|
);
|
||||||
const role = await RolesDBApi.findBy({ id: roleId });
|
const role = await RolesDBApi.findBy({ id: roleId });
|
||||||
if (!role?.role_customization) {
|
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 schema = await sjs.getSequelizeSchema(db.sequelize, {});
|
||||||
const payload = {
|
const payload = {
|
||||||
description: `Create some cool ${e} chart`,
|
description: `Create some cool ${e} chart`,
|
||||||
modelDefinition: schema.definitions,
|
modelDefinition: schema.definitions,
|
||||||
};
|
};
|
||||||
const widgetId = await getWidget(payload, currentUser?.id, roleId);
|
const widgetId = await getWidget(payload, currentUser?.id, roleId);
|
||||||
if(widgetId){
|
if (widgetId) {
|
||||||
await RolesService.addRoleInfo(
|
await RolesService.addRoleInfo(
|
||||||
roleId,
|
roleId,
|
||||||
currentUser?.id,
|
currentUser?.id,
|
||||||
@ -156,6 +171,7 @@ router.get(
|
|||||||
router.post(
|
router.post(
|
||||||
'/create_widget',
|
'/create_widget',
|
||||||
wrapAsync(async (req, res) => {
|
wrapAsync(async (req, res) => {
|
||||||
|
const { RolesService } = loadRolesModules();
|
||||||
const { description, userId, roleId } = req.body;
|
const { description, userId, roleId } = req.body;
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
@ -185,13 +201,13 @@ router.post(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/openai/ask:
|
* /api/openai/response:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [OpenAI]
|
* tags: [OpenAI]
|
||||||
* summary: Ask a question to ChatGPT
|
* summary: Proxy a Responses API request
|
||||||
* description: Send a question to OpenAI's ChatGPT and get a response
|
* description: Sends the payload to the Flatlogic AI proxy and returns the response.
|
||||||
* requestBody:
|
* requestBody:
|
||||||
* required: true
|
* required: true
|
||||||
* content:
|
* content:
|
||||||
@ -199,12 +215,73 @@ router.post(
|
|||||||
* schema:
|
* schema:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* 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
|
* type: string
|
||||||
* description: The question to ask ChatGPT
|
* description: The question to ask ChatGPT
|
||||||
* apiKey:
|
|
||||||
* type: string
|
|
||||||
* description: OpenAI API key
|
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Question successfully answered
|
* description: Question successfully answered
|
||||||
@ -233,7 +310,7 @@ router.post(
|
|||||||
if (!prompt) {
|
if (!prompt) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Question and API key are required',
|
error: 'Prompt is required',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
|
||||||
const Team_membersService = require('../services/team_members');
|
const Team_membershipsService = require('../services/team_memberships');
|
||||||
const Team_membersDBApi = require('../db/api/team_members');
|
const Team_membershipsDBApi = require('../db/api/team_memberships');
|
||||||
const wrapAsync = require('../helpers').wrapAsync;
|
const wrapAsync = require('../helpers').wrapAsync;
|
||||||
|
|
||||||
|
|
||||||
@ -15,14 +15,14 @@ const {
|
|||||||
checkCrudPermissions,
|
checkCrudPermissions,
|
||||||
} = require('../middlewares/check-permissions');
|
} = require('../middlewares/check-permissions');
|
||||||
|
|
||||||
router.use(checkCrudPermissions('team_members'));
|
router.use(checkCrudPermissions('team_memberships'));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* components:
|
* components:
|
||||||
* schemas:
|
* schemas:
|
||||||
* Team_members:
|
* Team_memberships:
|
||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
|
|
||||||
@ -34,17 +34,17 @@ router.use(checkCrudPermissions('team_members'));
|
|||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* tags:
|
* tags:
|
||||||
* name: Team_members
|
* name: Team_memberships
|
||||||
* description: The Team_members managing API
|
* description: The Team_memberships managing API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/team_members:
|
* /api/team_memberships:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Team_members]
|
* tags: [Team_memberships]
|
||||||
* summary: Add new item
|
* summary: Add new item
|
||||||
* description: Add new item
|
* description: Add new item
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -56,14 +56,14 @@ router.use(checkCrudPermissions('team_members'));
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The item was successfully added
|
* description: The item was successfully added
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -74,7 +74,7 @@ router.use(checkCrudPermissions('team_members'));
|
|||||||
router.post('/', wrapAsync(async (req, res) => {
|
router.post('/', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
@ -85,7 +85,7 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Team_members]
|
* tags: [Team_memberships]
|
||||||
* summary: Bulk import items
|
* summary: Bulk import items
|
||||||
* description: Bulk import items
|
* description: Bulk import items
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -98,14 +98,14 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
* description: Data of the updated items
|
* description: Data of the updated items
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: The items were successfully imported
|
* description: The items were successfully imported
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 405:
|
* 405:
|
||||||
@ -117,18 +117,18 @@ router.post('/', wrapAsync(async (req, res) => {
|
|||||||
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
router.post('/bulk-import', wrapAsync(async (req, res) => {
|
||||||
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
||||||
const link = new URL(referer);
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/team_members/{id}:
|
* /api/team_memberships/{id}:
|
||||||
* put:
|
* put:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Team_members]
|
* tags: [Team_memberships]
|
||||||
* summary: Update the data of the selected item
|
* summary: Update the data of the selected item
|
||||||
* description: Update the data of the selected item
|
* description: Update the data of the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -151,7 +151,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* data:
|
* data:
|
||||||
* description: Data of the updated item
|
* description: Data of the updated item
|
||||||
* type: object
|
* type: object
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* required:
|
* required:
|
||||||
* - id
|
* - id
|
||||||
* responses:
|
* responses:
|
||||||
@ -160,7 +160,7 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -171,18 +171,18 @@ router.post('/bulk-import', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.put('/:id', wrapAsync(async (req, res) => {
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/team_members/{id}:
|
* /api/team_memberships/{id}:
|
||||||
* delete:
|
* delete:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Team_members]
|
* tags: [Team_memberships]
|
||||||
* summary: Delete the selected item
|
* summary: Delete the selected item
|
||||||
* description: Delete the selected item
|
* description: Delete the selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -198,7 +198,7 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -209,18 +209,18 @@ router.put('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', wrapAsync(async (req, res) => {
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/team_members/deleteByIds:
|
* /api/team_memberships/deleteByIds:
|
||||||
* post:
|
* post:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Team_members]
|
* tags: [Team_memberships]
|
||||||
* summary: Delete the selected item list
|
* summary: Delete the selected item list
|
||||||
* description: Delete the selected item list
|
* description: Delete the selected item list
|
||||||
* requestBody:
|
* requestBody:
|
||||||
@ -238,7 +238,7 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -247,29 +247,29 @@ router.delete('/:id', wrapAsync(async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.post('/deleteByIds', wrapAsync(async (req, res) => {
|
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;
|
const payload = true;
|
||||||
res.status(200).send(payload);
|
res.status(200).send(payload);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/team_members:
|
* /api/team_memberships:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Team_members]
|
* tags: [Team_memberships]
|
||||||
* summary: Get all team_members
|
* summary: Get all team_memberships
|
||||||
* description: Get all team_members
|
* description: Get all team_memberships
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Team_members list successfully received
|
* description: Team_memberships list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -281,7 +281,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
const filetype = req.query.filetype
|
const filetype = req.query.filetype
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await Team_membersDBApi.findAll(
|
const payload = await Team_membershipsDBApi.findAll(
|
||||||
req.query, { currentUser }
|
req.query, { currentUser }
|
||||||
);
|
);
|
||||||
if (filetype && filetype === 'csv') {
|
if (filetype && filetype === 'csv') {
|
||||||
@ -307,22 +307,22 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/team_members/count:
|
* /api/team_memberships/count:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Team_members]
|
* tags: [Team_memberships]
|
||||||
* summary: Count all team_members
|
* summary: Count all team_memberships
|
||||||
* description: Count all team_members
|
* description: Count all team_memberships
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Team_members count successfully received
|
* description: Team_memberships count successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -333,7 +333,7 @@ router.get('/', wrapAsync(async (req, res) => {
|
|||||||
router.get('/count', wrapAsync(async (req, res) => {
|
router.get('/count', wrapAsync(async (req, res) => {
|
||||||
|
|
||||||
const currentUser = req.currentUser;
|
const currentUser = req.currentUser;
|
||||||
const payload = await Team_membersDBApi.findAll(
|
const payload = await Team_membershipsDBApi.findAll(
|
||||||
req.query,
|
req.query,
|
||||||
null,
|
null,
|
||||||
{ countOnly: true, currentUser }
|
{ countOnly: true, currentUser }
|
||||||
@ -344,22 +344,22 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/team_members/autocomplete:
|
* /api/team_memberships/autocomplete:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Team_members]
|
* tags: [Team_memberships]
|
||||||
* summary: Find all team_members that match search criteria
|
* summary: Find all team_memberships that match search criteria
|
||||||
* description: Find all team_members that match search criteria
|
* description: Find all team_memberships that match search criteria
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Team_members list successfully received
|
* description: Team_memberships list successfully received
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* 401:
|
* 401:
|
||||||
* $ref: "#/components/responses/UnauthorizedError"
|
* $ref: "#/components/responses/UnauthorizedError"
|
||||||
* 404:
|
* 404:
|
||||||
@ -369,7 +369,7 @@ router.get('/count', wrapAsync(async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/autocomplete', 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.query,
|
||||||
req.query.limit,
|
req.query.limit,
|
||||||
req.query.offset,
|
req.query.offset,
|
||||||
@ -381,11 +381,11 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
* /api/team_members/{id}:
|
* /api/team_memberships/{id}:
|
||||||
* get:
|
* get:
|
||||||
* security:
|
* security:
|
||||||
* - bearerAuth: []
|
* - bearerAuth: []
|
||||||
* tags: [Team_members]
|
* tags: [Team_memberships]
|
||||||
* summary: Get selected item
|
* summary: Get selected item
|
||||||
* description: Get selected item
|
* description: Get selected item
|
||||||
* parameters:
|
* parameters:
|
||||||
@ -401,7 +401,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
* $ref: "#/components/schemas/Team_members"
|
* $ref: "#/components/schemas/Team_memberships"
|
||||||
* 400:
|
* 400:
|
||||||
* description: Invalid ID supplied
|
* description: Invalid ID supplied
|
||||||
* 401:
|
* 401:
|
||||||
@ -412,7 +412,7 @@ router.get('/autocomplete', async (req, res) => {
|
|||||||
* description: Some server error
|
* description: Some server error
|
||||||
*/
|
*/
|
||||||
router.get('/:id', wrapAsync(async (req, res) => {
|
router.get('/:id', wrapAsync(async (req, res) => {
|
||||||
const payload = await Team_membersDBApi.findBy(
|
const payload = await Team_membershipsDBApi.findBy(
|
||||||
{ id: req.params.id },
|
{ id: req.params.id },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
const errors = {
|
const errors = {
|
||||||
app: {
|
app: {
|
||||||
title: 'Team Projects Hub',
|
title: 'TeamFlow Manager',
|
||||||
},
|
},
|
||||||
|
|
||||||
auth: {
|
auth: {
|
||||||
|
|||||||
@ -1,13 +1,24 @@
|
|||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const { v4: uuid } = require('uuid');
|
|
||||||
const RoleService = require('./roles');
|
|
||||||
const config = require('../config');
|
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 {
|
module.exports = class OpenAiService {
|
||||||
static async getWidget(payload, userId, roleId) {
|
static async getWidget(payload, userId, roleId) {
|
||||||
|
const RoleService = loadRoleService();
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`${config.flHost}/${config.project_uuid}/project_customization_widgets.json`,
|
`${config.flHost}/${config.project_uuid}/project_customization_widgets.json`,
|
||||||
payload,
|
payload,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status >= 200 && response.status < 300) {
|
if (response.status >= 200 && response.status < 300) {
|
||||||
@ -21,47 +32,49 @@ module.exports = class OpenAiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async askGpt(prompt) {
|
static async askGpt(prompt) {
|
||||||
if (!config.gpt_key) {
|
if (!prompt) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
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) {
|
const response = await LocalAIApi.createResponse(
|
||||||
return {
|
{
|
||||||
success: true,
|
input: [{ role: 'user', content: prompt }],
|
||||||
data: response.data.choices[0].message.content
|
},
|
||||||
};
|
{
|
||||||
} else {
|
poll_interval: 5,
|
||||||
console.error('Error asking question to ChatGPT:', response.data);
|
poll_timeout: 300,
|
||||||
return {
|
},
|
||||||
success: false,
|
);
|
||||||
error: response.data
|
|
||||||
};
|
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 {
|
return {
|
||||||
success: false,
|
success: true,
|
||||||
error: error.response?.data || error.message
|
data: text,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.error('AI proxy error:', response);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: response.error || response.message || 'AI proxy error',
|
||||||
|
response,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -78,26 +78,6 @@ module.exports = class SearchService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -122,22 +102,6 @@ module.exports = class SearchService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
const db = require('../db/models');
|
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 processFile = require("../middlewares/upload");
|
||||||
const ValidationError = require('./notifications/errors/validation');
|
const ValidationError = require('./notifications/errors/validation');
|
||||||
const csv = require('csv-parser');
|
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) {
|
static async create(data, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
await CommentsDBApi.create(
|
await Team_membershipsDBApi.create(
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -51,7 +51,7 @@ module.exports = class CommentsService {
|
|||||||
.on('error', (error) => reject(error));
|
.on('error', (error) => reject(error));
|
||||||
})
|
})
|
||||||
|
|
||||||
await CommentsDBApi.bulkImport(results, {
|
await Team_membershipsDBApi.bulkImport(results, {
|
||||||
transaction,
|
transaction,
|
||||||
ignoreDuplicates: true,
|
ignoreDuplicates: true,
|
||||||
validate: true,
|
validate: true,
|
||||||
@ -68,18 +68,18 @@ module.exports = class CommentsService {
|
|||||||
static async update(data, id, currentUser) {
|
static async update(data, id, currentUser) {
|
||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
let comments = await CommentsDBApi.findBy(
|
let team_memberships = await Team_membershipsDBApi.findBy(
|
||||||
{id},
|
{id},
|
||||||
{transaction},
|
{transaction},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!comments) {
|
if (!team_memberships) {
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
'commentsNotFound',
|
'team_membershipsNotFound',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedComments = await CommentsDBApi.update(
|
const updatedTeam_memberships = await Team_membershipsDBApi.update(
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
@ -89,7 +89,7 @@ module.exports = class CommentsService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
return updatedComments;
|
return updatedTeam_memberships;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
@ -101,7 +101,7 @@ module.exports = class CommentsService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await CommentsDBApi.deleteByIds(ids, {
|
await Team_membershipsDBApi.deleteByIds(ids, {
|
||||||
currentUser,
|
currentUser,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
@ -117,7 +117,7 @@ module.exports = class CommentsService {
|
|||||||
const transaction = await db.sequelize.transaction();
|
const transaction = await db.sequelize.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await CommentsDBApi.remove(
|
await Team_membershipsDBApi.remove(
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
currentUser,
|
currentUser,
|
||||||
@ -2,13 +2,16 @@ const chokidar = require('chokidar');
|
|||||||
const { exec } = require('child_process');
|
const { exec } = require('child_process');
|
||||||
const nodemon = require('nodemon');
|
const nodemon = require('nodemon');
|
||||||
|
|
||||||
|
const nodeEnv = 'dev_stage';
|
||||||
|
const childEnv = { ...process.env, NODE_ENV: nodeEnv };
|
||||||
|
|
||||||
const migrationsWatcher = chokidar.watch('./src/db/migrations', {
|
const migrationsWatcher = chokidar.watch('./src/db/migrations', {
|
||||||
persistent: true,
|
persistent: true,
|
||||||
ignoreInitial: true
|
ignoreInitial: true
|
||||||
});
|
});
|
||||||
migrationsWatcher.on('add', (filePath) => {
|
migrationsWatcher.on('add', (filePath) => {
|
||||||
console.log(`[DEBUG] New migration file: ${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);
|
console.log(stdout);
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(stderr);
|
console.error(stderr);
|
||||||
@ -22,7 +25,7 @@ const seedersWatcher = chokidar.watch('./src/db/seeders', {
|
|||||||
});
|
});
|
||||||
seedersWatcher.on('add', (filePath) => {
|
seedersWatcher.on('add', (filePath) => {
|
||||||
console.log(`[DEBUG] New seed file: ${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);
|
console.log(stdout);
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(stderr);
|
console.error(stderr);
|
||||||
@ -32,6 +35,7 @@ seedersWatcher.on('add', (filePath) => {
|
|||||||
|
|
||||||
nodemon({
|
nodemon({
|
||||||
script: './src/index.js',
|
script: './src/index.js',
|
||||||
|
env: childEnv,
|
||||||
ignore: ['./src/db/migrations', './src/db/seeders'],
|
ignore: ['./src/db/migrations', './src/db/seeders'],
|
||||||
delay: '500'
|
delay: '500'
|
||||||
});
|
});
|
||||||
@ -42,4 +46,4 @@ nodemon.on('start', () => {
|
|||||||
|
|
||||||
nodemon.on('restart', (files) => {
|
nodemon.on('restart', (files) => {
|
||||||
console.log('Nodemon restarted due changes in:', files);
|
console.log('Nodemon restarted due changes in:', files);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -25,7 +25,7 @@ services:
|
|||||||
- ./data/db:/var/lib/postgresql/data
|
- ./data/db:/var/lib/postgresql/data
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||||
- POSTGRES_DB=db_team_projects_hub
|
- POSTGRES_DB=db_teamflow_manager
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
logging:
|
logging:
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# Team Projects Hub
|
# TeamFlow Manager
|
||||||
|
|
||||||
## This project was generated by Flatlogic Platform.
|
## This project was generated by Flatlogic Platform.
|
||||||
## Install
|
## Install
|
||||||
|
|||||||
2
frontend/next-env.d.ts
vendored
2
frontend/next-env.d.ts
vendored
@ -1,6 +1,6 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
/// <reference path="./build/types/routes.d.ts" />
|
/// <reference path="./.next/types/routes.d.ts" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@ -1,42 +1,49 @@
|
|||||||
import type { ColorButtonKey } from './interfaces'
|
import type { ColorButtonKey } from './interfaces'
|
||||||
|
|
||||||
export const gradientBgBase = 'bg-gradient-to-tr'
|
// Base background color
|
||||||
export const colorBgBase = "bg-violet-50/50"
|
export const colorBgBase = 'bg-gray-100 dark:bg-gray-900'
|
||||||
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`
|
|
||||||
|
|
||||||
|
// No more gradients
|
||||||
|
export const gradientBgBase = ''
|
||||||
|
export const gradientBgPurplePink = ''
|
||||||
|
export const gradientBgViolet = ''
|
||||||
|
export const gradientBgDark = ''
|
||||||
|
export const gradientBgPinkRed = ''
|
||||||
|
|
||||||
|
// Simplified background colors
|
||||||
export const colorsBgLight = {
|
export const colorsBgLight = {
|
||||||
white: 'bg-white text-black',
|
white: 'bg-white text-black dark:bg-gray-800 dark:text-white',
|
||||||
light: ' bg-white text-black text-black dark:bg-dark-900 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',
|
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',
|
success: 'bg-green-500 border-green-500 text-white',
|
||||||
danger: 'bg-red-500 border-red-500 text-white',
|
danger: 'bg-red-600 border-red-600 text-white',
|
||||||
warning: 'bg-yellow-500 border-yellow-500 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 = {
|
export const colorsText = {
|
||||||
white: 'text-black dark:text-slate-100',
|
white: 'text-black dark:text-gray-100',
|
||||||
light: 'text-gray-700 dark:text-slate-400',
|
light: 'text-gray-700 dark:text-gray-400',
|
||||||
contrast: 'dark:text-white',
|
contrast: 'dark:text-white',
|
||||||
success: 'text-emerald-500',
|
success: 'text-green-500',
|
||||||
danger: 'text-red-500',
|
danger: 'text-red-600',
|
||||||
warning: 'text-yellow-500',
|
warning: 'text-yellow-500',
|
||||||
info: 'text-blue-500',
|
info: 'text-blue-500',
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// Simplified outline colors
|
||||||
export const colorsOutline = {
|
export const colorsOutline = {
|
||||||
white: [colorsText.white, 'border-gray-100'].join(' '),
|
white: [colorsText.white, 'border-gray-200 dark:border-gray-700'].join(' '),
|
||||||
light: [colorsText.light, 'border-gray-100'].join(' '),
|
light: [colorsText.light, 'border-gray-200 dark:border-gray-700'].join(' '),
|
||||||
contrast: [colorsText.contrast, 'border-gray-900 dark:border-slate-100'].join(' '),
|
contrast: [colorsText.contrast, 'border-gray-700 dark:border-gray-200'].join(' '),
|
||||||
success: [colorsText.success, 'border-emerald-500'].join(' '),
|
success: [colorsText.success, 'border-green-500'].join(' '),
|
||||||
danger: [colorsText.danger, 'border-red-500'].join(' '),
|
danger: [colorsText.danger, 'border-red-600'].join(' '),
|
||||||
warning: [colorsText.warning, 'border-yellow-500'].join(' '),
|
warning: [colorsText.warning, 'border-yellow-500'].join(' '),
|
||||||
info: [colorsText.info, 'border-blue-500'].join(' '),
|
info: [colorsText.info, 'border-blue-500'].join(' '),
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// Simplified button color logic
|
||||||
export const getButtonColor = (
|
export const getButtonColor = (
|
||||||
color: ColorButtonKey,
|
color: ColorButtonKey,
|
||||||
isOutlined: boolean,
|
isOutlined: boolean,
|
||||||
@ -47,92 +54,104 @@ export const getButtonColor = (
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const colors = {
|
const baseColors = {
|
||||||
ring: {
|
white: {
|
||||||
white: 'ring-gray-200 dark:ring-gray-500',
|
bg: 'bg-white text-black',
|
||||||
whiteDark: 'ring-gray-200 dark:ring-dark-500',
|
border: 'border-gray-300',
|
||||||
lightDark: 'ring-gray-200 dark:ring-gray-500',
|
hover: 'hover:bg-gray-50',
|
||||||
contrast: 'ring-gray-300 dark:ring-gray-400',
|
active: 'bg-gray-100',
|
||||||
success: 'ring-emerald-300 dark:ring-pavitra-blue',
|
ring: 'ring-gray-300',
|
||||||
danger: 'ring-red-300 dark:ring-red-700',
|
|
||||||
warning: 'ring-yellow-300 dark:ring-yellow-700',
|
|
||||||
info: "ring-blue-300 dark:ring-pavitra-blue",
|
|
||||||
},
|
},
|
||||||
active: {
|
light: {
|
||||||
white: 'bg-gray-100',
|
bg: 'bg-gray-200 text-black',
|
||||||
whiteDark: 'bg-gray-100 dark:bg-dark-800',
|
border: 'border-gray-300',
|
||||||
lightDark: 'bg-gray-200 dark:bg-slate-700',
|
hover: 'hover:bg-gray-300',
|
||||||
contrast: 'bg-gray-700 dark:bg-slate-100',
|
active: 'bg-gray-300',
|
||||||
success: 'bg-emerald-700 dark:bg-pavitra-blue',
|
ring: 'ring-gray-300',
|
||||||
danger: 'bg-red-700 dark:bg-red-600',
|
|
||||||
warning: 'bg-yellow-700 dark:bg-yellow-600',
|
|
||||||
info: 'bg-blue-700 dark:bg-pavitra-blue',
|
|
||||||
},
|
},
|
||||||
bg: {
|
contrast: {
|
||||||
white: 'bg-white text-black',
|
bg: 'bg-gray-800 text-white',
|
||||||
whiteDark: 'bg-white text-black dark:bg-dark-900 dark:text-white',
|
border: 'border-gray-800',
|
||||||
lightDark: 'bg-gray-100 text-black dark:bg-slate-800 dark:text-white',
|
hover: 'hover:bg-gray-700',
|
||||||
contrast: 'bg-gray-800 text-white dark:bg-white dark:text-black',
|
active: 'bg-gray-700',
|
||||||
success: 'bg-emerald-600 dark:bg-pavitra-blue text-white',
|
ring: 'ring-gray-300',
|
||||||
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 ",
|
|
||||||
},
|
},
|
||||||
bgHover: {
|
success: {
|
||||||
white: 'hover:bg-gray-100',
|
bg: 'bg-green-500 text-white',
|
||||||
whiteDark: 'hover:bg-gray-100 hover:dark:bg-dark-800',
|
border: 'border-green-500',
|
||||||
lightDark: 'hover:bg-gray-200 hover:dark:bg-slate-700',
|
hover: 'hover:bg-green-600',
|
||||||
contrast: 'hover:bg-gray-700 hover:dark:bg-slate-100',
|
active: 'bg-green-600',
|
||||||
success:
|
ring: 'ring-green-300',
|
||||||
'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",
|
|
||||||
},
|
},
|
||||||
borders: {
|
danger: {
|
||||||
white: 'border-white',
|
bg: 'bg-red-600 text-white',
|
||||||
whiteDark: 'border-white dark:border-dark-900',
|
border: 'border-red-600',
|
||||||
lightDark: 'border-gray-100 dark:border-slate-800',
|
hover: 'hover:bg-red-700',
|
||||||
contrast: 'border-gray-800 dark:border-white',
|
active: 'bg-red-700',
|
||||||
success: 'border-emerald-600 dark:border-pavitra-blue',
|
ring: 'ring-red-300',
|
||||||
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",
|
|
||||||
},
|
},
|
||||||
text: {
|
warning: {
|
||||||
contrast: 'dark:text-slate-100',
|
bg: 'bg-yellow-500 text-white',
|
||||||
success: 'text-emerald-600 dark:text-pavitra-blue',
|
border: 'border-yellow-500',
|
||||||
danger: 'text-red-600 dark:text-red-500',
|
hover: 'hover:bg-yellow-600',
|
||||||
warning: 'text-yellow-600 dark:text-yellow-500',
|
active: 'bg-yellow-600',
|
||||||
info: 'text-blue-600 dark:text-pavitra-blue',
|
ring: 'ring-yellow-300',
|
||||||
},
|
},
|
||||||
outlineHover: {
|
info: {
|
||||||
contrast:
|
bg: 'bg-blue-500 text-white',
|
||||||
'hover:bg-gray-800 hover:text-gray-100 hover:dark:bg-slate-100 hover:dark:text-black',
|
border: 'border-blue-500',
|
||||||
success: 'hover:bg-emerald-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-pavitra-blue',
|
hover: 'hover:bg-blue-600',
|
||||||
danger:
|
active: 'bg-blue-600',
|
||||||
'hover:bg-red-600 hover:text-white hover:text-white hover:dark:text-white hover:dark:border-red-600',
|
ring: 'ring-blue-300',
|
||||||
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",
|
|
||||||
},
|
},
|
||||||
|
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) {
|
if (isActive) {
|
||||||
base.push(colors.active[color])
|
base.push(colorSet.active);
|
||||||
} else {
|
|
||||||
base.push(isOutlinedProcessed ? colors.text[color] : colors.bg[color])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasHover) {
|
|
||||||
base.push(isOutlinedProcessed ? colors.outlineHover[color] : colors.bgHover[color])
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.join(' ')
|
return base.join(' ')
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ export default function AsideMenu({
|
|||||||
<>
|
<>
|
||||||
<AsideMenuLayer
|
<AsideMenuLayer
|
||||||
menu={props.menu}
|
menu={props.menu}
|
||||||
className={`${isAsideMobileExpanded ? 'left-0' : '-left-60 lg:left-0'} ${
|
className={`${isAsideMobileExpanded ? 'left-0' : '-left-52 lg:left-0'} ${
|
||||||
!isAsideLgActive ? 'lg:hidden xl:flex' : ''
|
!isAsideLgActive ? 'lg:hidden xl:flex' : ''
|
||||||
}`}
|
}`}
|
||||||
onAsideLgCloseClick={props.onAsideLgClose}
|
onAsideLgCloseClick={props.onAsideLgClose}
|
||||||
|
|||||||
@ -4,8 +4,6 @@ import BaseIcon from './BaseIcon'
|
|||||||
import AsideMenuList from './AsideMenuList'
|
import AsideMenuList from './AsideMenuList'
|
||||||
import { MenuAsideItem } from '../interfaces'
|
import { MenuAsideItem } from '../interfaces'
|
||||||
import { useAppSelector } from '../stores/hooks'
|
import { useAppSelector } from '../stores/hooks'
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
menu: MenuAsideItem[]
|
menu: MenuAsideItem[]
|
||||||
@ -14,10 +12,6 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function AsideMenuLayer({ menu, className = '', ...props }: Props) {
|
export default function AsideMenuLayer({ menu, className = '', ...props }: Props) {
|
||||||
const corners = useAppSelector((state) => 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 darkMode = useAppSelector((state) => state.style.darkMode)
|
||||||
|
|
||||||
const handleAsideLgCloseClick = (e: React.MouseEvent) => {
|
const handleAsideLgCloseClick = (e: React.MouseEvent) => {
|
||||||
@ -25,35 +19,23 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props
|
|||||||
props.onAsideLgCloseClick()
|
props.onAsideLgCloseClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside
|
<aside
|
||||||
id='asideMenu'
|
id="asideMenu"
|
||||||
className={`${className} zzz lg:py-2 lg:pl-2 w-60 fixed flex z-40 top-0 h-screen transition-position overflow-hidden`}
|
className={`${className} zzz lg:py-2 lg:pl-2 w-52 fixed flex z-40 top-0 h-screen transition-position overflow-hidden`}
|
||||||
>
|
>
|
||||||
<div
|
<div className="flex-1 flex flex-col overflow-hidden bg-gray-50 dark:bg-dark-900">
|
||||||
className={`flex-1 flex flex-col overflow-hidden dark:bg-dark-900 ${asideStyle} ${corners}`}
|
<div className="flex flex-row h-14 items-center justify-between bg-gray-100 dark:bg-dark-900">
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`flex flex-row h-14 items-center justify-between ${asideBrandStyle}`}
|
|
||||||
>
|
|
||||||
<div className="text-center flex-1 lg:text-left lg:pl-6 xl:text-center xl:pl-0">
|
<div className="text-center flex-1 lg:text-left lg:pl-6 xl:text-center xl:pl-0">
|
||||||
|
<b className="font-black">TeamFlow</b>
|
||||||
<b className="font-black">Team Projects Hub</b>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button className="hidden lg:inline-block xl:hidden p-3" onClick={handleAsideLgCloseClick}>
|
||||||
className="hidden lg:inline-block xl:hidden p-3"
|
|
||||||
onClick={handleAsideLgCloseClick}
|
|
||||||
>
|
|
||||||
<BaseIcon path={mdiClose} />
|
<BaseIcon path={mdiClose} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`flex-1 overflow-y-auto overflow-x-hidden ${
|
className={`flex-1 overflow-y-auto overflow-x-hidden ${darkMode ? 'aside-scrollbars-[slate]' : 'aside-scrollbars'
|
||||||
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<AsideMenuList menu={menu} />
|
<AsideMenuList menu={menu} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,80 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import CardBox from '../CardBox';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import ListActionsPopover from "../ListActionsPopover";
|
|
||||||
import {useAppSelector} from "../../stores/hooks";
|
|
||||||
import {Pagination} from "../Pagination";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
attachments: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ListAttachments = ({ attachments, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_ATTACHMENTS')
|
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
{!loading && attachments.map((item) => (
|
|
||||||
<div key={item.id}>
|
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href={`/attachments/attachments-view/?id=${item.id}`}
|
|
||||||
className={
|
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
|
|
||||||
</Link>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/attachments/attachments-edit/?id=${item.id}`}
|
|
||||||
pathView={`/attachments/attachments-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</CardBox>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{!loading && attachments.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ListAttachments
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseIcon from '../BaseIcon';
|
|
||||||
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
|
|
||||||
import axios from 'axios';
|
|
||||||
import {
|
|
||||||
GridActionsCellItem,
|
|
||||||
GridRowParams,
|
|
||||||
GridValueGetterParams,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import DataGridMultiSelect from "../DataGridMultiSelect";
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
type Params = (id: string) => void;
|
|
||||||
|
|
||||||
export const loadColumns = async (
|
|
||||||
onDelete: Params,
|
|
||||||
entityName: string,
|
|
||||||
|
|
||||||
user
|
|
||||||
|
|
||||||
) => {
|
|
||||||
async function callOptionsApi(entityName: string) {
|
|
||||||
|
|
||||||
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await axios(`/${entityName}/autocomplete?limit=100`);
|
|
||||||
return data.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_ATTACHMENTS')
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'actions',
|
|
||||||
type: 'actions',
|
|
||||||
minWidth: 30,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
getActions: (params: GridRowParams) => {
|
|
||||||
|
|
||||||
return [
|
|
||||||
<div key={params?.row?.id}>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={params?.row?.id}
|
|
||||||
pathEdit={`/attachments/attachments-edit/?id=${params?.row?.id}`}
|
|
||||||
pathView={`/attachments/attachments-view/?id=${params?.row?.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
@ -42,7 +42,6 @@ export default function BaseButton({
|
|||||||
roundedFull = false,
|
roundedFull = false,
|
||||||
onClick,
|
onClick,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const componentClass = [
|
const componentClass = [
|
||||||
'inline-flex',
|
'inline-flex',
|
||||||
'justify-center',
|
'justify-center',
|
||||||
@ -52,19 +51,18 @@ export default function BaseButton({
|
|||||||
'transition-colors',
|
'transition-colors',
|
||||||
'focus:ring',
|
'focus:ring',
|
||||||
'duration-150',
|
'duration-150',
|
||||||
'border',
|
|
||||||
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
||||||
roundedFull ? 'rounded-full' : `${corners}`,
|
roundedFull ? 'rounded-full' : 'rounded-md',
|
||||||
getButtonColor(color, outline, !disabled, active),
|
getButtonColor(color, outline, !disabled, active),
|
||||||
className,
|
className,
|
||||||
]
|
]
|
||||||
|
|
||||||
if (!label && icon) {
|
if (!label && icon) {
|
||||||
componentClass.push('p-1')
|
componentClass.push('p-2')
|
||||||
} else if (small) {
|
} else if (small) {
|
||||||
componentClass.push('text-sm', roundedFull ? 'px-3 py-1' : 'p-1')
|
componentClass.push('text-sm', 'px-3 py-1')
|
||||||
} else {
|
} else {
|
||||||
componentClass.push('py-2', roundedFull ? 'px-6' : 'px-3')
|
componentClass.push('py-2', 'px-4')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
|
|||||||
@ -35,18 +35,17 @@ export default function CardBox({
|
|||||||
onClick,
|
onClick,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
|
|
||||||
const componentClass = [
|
const componentClass = [
|
||||||
`flex dark:border-dark-700 dark:bg-dark-900`,
|
'flex',
|
||||||
|
'bg-white dark:bg-gray-800',
|
||||||
className,
|
className,
|
||||||
corners !== 'rounded-full'? corners : 'rounded-3xl',
|
corners !== 'rounded-full' ? corners : 'rounded-lg',
|
||||||
flex,
|
flex,
|
||||||
isList ? '' : `${cardsStyle}`,
|
'border border-gray-200 dark:border-gray-700',
|
||||||
hasTable ? '' : `border-dark-700 dark:border-dark-700`,
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if (isHoverable) {
|
if (isHoverable) {
|
||||||
componentClass.push('hover:shadow-lg transition-shadow duration-500')
|
componentClass.push('hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors duration-150')
|
||||||
}
|
}
|
||||||
|
|
||||||
return React.createElement(
|
return React.createElement(
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
import { useAppSelector } from '../../stores/hooks';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import { Pagination } from '../Pagination';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
comments: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CardComments = ({
|
|
||||||
comments,
|
|
||||||
loading,
|
|
||||||
onDelete,
|
|
||||||
currentPage,
|
|
||||||
numPages,
|
|
||||||
onPageChange,
|
|
||||||
}: Props) => {
|
|
||||||
const asideScrollbarsStyle = useAppSelector(
|
|
||||||
(state) => state.style.asideScrollbarsStyle,
|
|
||||||
);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
const darkMode = useAppSelector((state) => state.style.darkMode);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_COMMENTS')
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={'p-4'}>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
<ul
|
|
||||||
role='list'
|
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
|
||||||
>
|
|
||||||
{!loading && comments.map((item, index) => (
|
|
||||||
<li
|
|
||||||
key={item.id}
|
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
|
||||||
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
|
||||||
|
|
||||||
<Link href={`/comments/comments-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
|
||||||
{item.content}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='ml-auto '>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/comments/comments-edit/?id=${item.id}`}
|
|
||||||
pathView={`/comments/comments-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
{!loading && comments.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CardComments;
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import CardBox from '../CardBox';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import ListActionsPopover from "../ListActionsPopover";
|
|
||||||
import {useAppSelector} from "../../stores/hooks";
|
|
||||||
import {Pagination} from "../Pagination";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
comments: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ListComments = ({ comments, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_COMMENTS')
|
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
{!loading && comments.map((item) => (
|
|
||||||
<div key={item.id}>
|
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href={`/comments/comments-view/?id=${item.id}`}
|
|
||||||
className={
|
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
|
|
||||||
</Link>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/comments/comments-edit/?id=${item.id}`}
|
|
||||||
pathView={`/comments/comments-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</CardBox>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{!loading && comments.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ListComments
|
|
||||||
@ -1,476 +0,0 @@
|
|||||||
import React, { useEffect, useState, useMemo } from 'react'
|
|
||||||
import { createPortal } from 'react-dom';
|
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
|
||||||
import BaseButton from '../BaseButton'
|
|
||||||
import CardBoxModal from '../CardBoxModal'
|
|
||||||
import CardBox from "../CardBox";
|
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/comments/commentsSlice'
|
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { Field, Form, Formik } from "formik";
|
|
||||||
import {
|
|
||||||
DataGrid,
|
|
||||||
GridColDef,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import {loadColumns} from "./configureCommentsCols";
|
|
||||||
import _ from 'lodash';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import {dataGridStyles} from "../../styles";
|
|
||||||
|
|
||||||
|
|
||||||
import ListComments from './ListComments';
|
|
||||||
|
|
||||||
|
|
||||||
const perPage = 10
|
|
||||||
|
|
||||||
const TableSampleComments = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const pagesList = [];
|
|
||||||
const [id, setId] = useState(null);
|
|
||||||
const [currentPage, setCurrentPage] = useState(0);
|
|
||||||
const [filterRequest, setFilterRequest] = React.useState('');
|
|
||||||
const [columns, setColumns] = useState<GridColDef[]>([]);
|
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
|
||||||
const [sortModel, setSortModel] = useState([
|
|
||||||
{
|
|
||||||
field: '',
|
|
||||||
sort: 'desc',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { comments, loading, count, notify: commentsNotify, refetch } = useAppSelector((state) => state.comments)
|
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
|
|
||||||
for (let i = 0; i < numPages; i++) {
|
|
||||||
pagesList.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadData = async (page = currentPage, request = filterRequest) => {
|
|
||||||
if (page !== currentPage) setCurrentPage(page);
|
|
||||||
if (request !== filterRequest) setFilterRequest(request);
|
|
||||||
const { sort, field } = sortModel[0];
|
|
||||||
|
|
||||||
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
|
|
||||||
dispatch(fetch({ limit: perPage, page, query }));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (commentsNotify.showNotification) {
|
|
||||||
notify(commentsNotify.typeNotification, commentsNotify.textNotification);
|
|
||||||
}
|
|
||||||
}, [commentsNotify.showNotification]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
loadData();
|
|
||||||
}, [sortModel, currentUser]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (refetch) {
|
|
||||||
loadData(0);
|
|
||||||
dispatch(setRefetch(false));
|
|
||||||
}
|
|
||||||
}, [refetch, dispatch]);
|
|
||||||
|
|
||||||
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
|
|
||||||
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
|
|
||||||
|
|
||||||
const handleModalAction = () => {
|
|
||||||
setIsModalInfoActive(false)
|
|
||||||
setIsModalTrashActive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleDeleteModalAction = (id: string) => {
|
|
||||||
setId(id)
|
|
||||||
setIsModalTrashActive(true)
|
|
||||||
}
|
|
||||||
const handleDeleteAction = async () => {
|
|
||||||
if (id) {
|
|
||||||
await dispatch(deleteItem(id));
|
|
||||||
await loadData(0);
|
|
||||||
setIsModalTrashActive(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateFilterRequests = useMemo(() => {
|
|
||||||
let request = '&';
|
|
||||||
filterItems.forEach((item) => {
|
|
||||||
const isRangeFilter = filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title === item.fields.selectedField &&
|
|
||||||
(filter.number || filter.date),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isRangeFilter) {
|
|
||||||
const from = item.fields.filterValueFrom;
|
|
||||||
const to = item.fields.filterValueTo;
|
|
||||||
if (from) {
|
|
||||||
request += `${item.fields.selectedField}Range=${from}&`;
|
|
||||||
}
|
|
||||||
if (to) {
|
|
||||||
request += `${item.fields.selectedField}Range=${to}&`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const value = item.fields.filterValue;
|
|
||||||
if (value) {
|
|
||||||
request += `${item.fields.selectedField}=${value}&`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return request;
|
|
||||||
}, [filterItems, filters]);
|
|
||||||
|
|
||||||
const deleteFilter = (value) => {
|
|
||||||
const newItems = filterItems.filter((item) => item.id !== value);
|
|
||||||
|
|
||||||
if (newItems.length) {
|
|
||||||
setFilterItems(newItems);
|
|
||||||
} else {
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
setFilterItems(newItems);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
loadData(0, generateFilterRequests);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (id) => (e) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
const name = e.target.name;
|
|
||||||
|
|
||||||
setFilterItems(
|
|
||||||
filterItems.map((item) => {
|
|
||||||
if (item.id !== id) return item;
|
|
||||||
if (name === 'selectedField') return { id, fields: { [name]: value } };
|
|
||||||
|
|
||||||
return { id, fields: { ...item.fields, [name]: value } }
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setFilterItems([]);
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPageChange = (page: number) => {
|
|
||||||
loadData(page);
|
|
||||||
setCurrentPage(page);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
|
|
||||||
loadColumns(
|
|
||||||
handleDeleteModalAction,
|
|
||||||
`comments`,
|
|
||||||
currentUser,
|
|
||||||
).then((newCols) => setColumns(newCols));
|
|
||||||
}, [currentUser]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleTableSubmit = async (id: string, data) => {
|
|
||||||
|
|
||||||
if (!_.isEmpty(data)) {
|
|
||||||
await dispatch(update({ id, data }))
|
|
||||||
.unwrap()
|
|
||||||
.then((res) => res)
|
|
||||||
.catch((err) => {
|
|
||||||
throw new Error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteRows = async (selectedRows) => {
|
|
||||||
await dispatch(deleteItemsByIds(selectedRows));
|
|
||||||
await loadData(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
const controlClasses =
|
|
||||||
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
|
|
||||||
` ${bgColor} ${focusRing} ${corners} ` +
|
|
||||||
'dark:bg-slate-800 border';
|
|
||||||
|
|
||||||
|
|
||||||
const dataGrid = (
|
|
||||||
<div className='relative overflow-x-auto'>
|
|
||||||
<DataGrid
|
|
||||||
autoHeight
|
|
||||||
rowHeight={64}
|
|
||||||
sx={dataGridStyles}
|
|
||||||
className={'datagrid--table'}
|
|
||||||
getRowClassName={() => `datagrid--row`}
|
|
||||||
rows={comments ?? []}
|
|
||||||
columns={columns}
|
|
||||||
initialState={{
|
|
||||||
pagination: {
|
|
||||||
paginationModel: {
|
|
||||||
pageSize: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
disableRowSelectionOnClick
|
|
||||||
onProcessRowUpdateError={(params) => {
|
|
||||||
console.log('Error', params);
|
|
||||||
}}
|
|
||||||
processRowUpdate={async (newRow, oldRow) => {
|
|
||||||
const data = dataFormatter.dataGridEditFormatter(newRow);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await handleTableSubmit(newRow.id, data);
|
|
||||||
return newRow;
|
|
||||||
} catch {
|
|
||||||
return oldRow;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
sortingMode={'server'}
|
|
||||||
checkboxSelection
|
|
||||||
onRowSelectionModelChange={(ids) => {
|
|
||||||
setSelectedRows(ids)
|
|
||||||
}}
|
|
||||||
onSortModelChange={(params) => {
|
|
||||||
params.length
|
|
||||||
? setSortModel(params)
|
|
||||||
: setSortModel([{ field: '', sort: 'desc' }]);
|
|
||||||
}}
|
|
||||||
rowCount={count}
|
|
||||||
pageSizeOptions={[10]}
|
|
||||||
paginationMode={'server'}
|
|
||||||
loading={loading}
|
|
||||||
onPaginationModelChange={(params) => {
|
|
||||||
onPageChange(params.page);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
|
|
||||||
<CardBox>
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
checkboxes: ['lorem'],
|
|
||||||
switches: ['lorem'],
|
|
||||||
radio: 'lorem',
|
|
||||||
}}
|
|
||||||
onSubmit={() => null}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<>
|
|
||||||
{filterItems && filterItems.map((filterItem) => {
|
|
||||||
return (
|
|
||||||
<div key={filterItem.id} className="flex mb-4">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Filter</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='selectedField'
|
|
||||||
id='selectedField'
|
|
||||||
component='select'
|
|
||||||
value={filterItem?.fields?.selectedField || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
{filters.map((selectOption) => (
|
|
||||||
<option
|
|
||||||
key={selectOption.title}
|
|
||||||
value={`${selectOption.title}`}
|
|
||||||
>
|
|
||||||
{selectOption.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.type === 'enum' ? (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className="text-gray-500 font-bold">
|
|
||||||
Value
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name="filterValue"
|
|
||||||
id='filterValue'
|
|
||||||
component="select"
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
<option value="">Select Value</option>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.options?.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
) : filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.number ? (
|
|
||||||
<div className="flex flex-row w-full mr-3">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">From</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col w-full">
|
|
||||||
<div className=" text-gray-500 font-bold">To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title ===
|
|
||||||
filterItem?.fields?.selectedField
|
|
||||||
)?.date ? (
|
|
||||||
<div className='flex flex-row w-full mr-3'>
|
|
||||||
<div className='flex flex-col w-full mr-3'>
|
|
||||||
<div className=' text-gray-500 font-bold'>
|
|
||||||
From
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='flex flex-col w-full'>
|
|
||||||
<div className=' text-gray-500 font-bold'>To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Contains</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValue'
|
|
||||||
placeholder='Contained'
|
|
||||||
id='filterValue'
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className=" text-gray-500 font-bold">Action</div>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
type='reset'
|
|
||||||
color='danger'
|
|
||||||
label='Delete'
|
|
||||||
onClick={() => {
|
|
||||||
deleteFilter(filterItem.id)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<div className="flex">
|
|
||||||
<BaseButton
|
|
||||||
className="my-2 mr-3"
|
|
||||||
color="success"
|
|
||||||
label='Apply'
|
|
||||||
onClick={handleSubmit}
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
color='info'
|
|
||||||
label='Cancel'
|
|
||||||
onClick={handleReset}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</CardBox> : null
|
|
||||||
}
|
|
||||||
<CardBoxModal
|
|
||||||
title="Please confirm"
|
|
||||||
buttonColor="info"
|
|
||||||
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
|
|
||||||
isActive={isModalTrashActive}
|
|
||||||
onConfirm={handleDeleteAction}
|
|
||||||
onCancel={handleModalAction}
|
|
||||||
>
|
|
||||||
<p>Are you sure you want to delete this item?</p>
|
|
||||||
</CardBoxModal>
|
|
||||||
|
|
||||||
|
|
||||||
{comments && Array.isArray(comments) && !showGrid && (
|
|
||||||
<ListComments
|
|
||||||
comments={comments}
|
|
||||||
loading={loading}
|
|
||||||
onDelete={handleDeleteModalAction}
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
onPageChange={onPageChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{showGrid && dataGrid}
|
|
||||||
|
|
||||||
|
|
||||||
{selectedRows.length > 0 &&
|
|
||||||
createPortal(
|
|
||||||
<BaseButton
|
|
||||||
className='me-4'
|
|
||||||
color='danger'
|
|
||||||
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
|
|
||||||
onClick={() => onDeleteRows(selectedRows)}
|
|
||||||
/>,
|
|
||||||
document.getElementById('delete-rows-button'),
|
|
||||||
)}
|
|
||||||
<ToastContainer />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TableSampleComments
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseIcon from '../BaseIcon';
|
|
||||||
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
|
|
||||||
import axios from 'axios';
|
|
||||||
import {
|
|
||||||
GridActionsCellItem,
|
|
||||||
GridRowParams,
|
|
||||||
GridValueGetterParams,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import DataGridMultiSelect from "../DataGridMultiSelect";
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
type Params = (id: string) => void;
|
|
||||||
|
|
||||||
export const loadColumns = async (
|
|
||||||
onDelete: Params,
|
|
||||||
entityName: string,
|
|
||||||
|
|
||||||
user
|
|
||||||
|
|
||||||
) => {
|
|
||||||
async function callOptionsApi(entityName: string) {
|
|
||||||
|
|
||||||
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await axios(`/${entityName}/autocomplete?limit=100`);
|
|
||||||
return data.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_COMMENTS')
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'actions',
|
|
||||||
type: 'actions',
|
|
||||||
minWidth: 30,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
getActions: (params: GridRowParams) => {
|
|
||||||
|
|
||||||
return [
|
|
||||||
<div key={params?.row?.id}>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={params?.row?.id}
|
|
||||||
pathEdit={`/comments/comments-edit/?id=${params?.row?.id}`}
|
|
||||||
pathView={`/comments/comments-view/?id=${params?.row?.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import React, { ReactNode } from 'react'
|
import React, { ReactNode } from 'react'
|
||||||
import LanguageSwitcher from "./LanguageSwitcher";
|
|
||||||
import { containerMaxW } from '../config'
|
import { containerMaxW } from '../config'
|
||||||
import Logo from './Logo'
|
import Logo from './Logo'
|
||||||
|
|
||||||
@ -26,7 +25,6 @@ export default function FooterBar({ children }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex item-center md:py-2 gap-4">
|
<div className="flex item-center md:py-2 gap-4">
|
||||||
<LanguageSwitcher />
|
|
||||||
<a href="https://flatlogic.com/" rel="noreferrer" target="_blank">
|
<a href="https://flatlogic.com/" rel="noreferrer" target="_blank">
|
||||||
<Logo className="w-auto h-8 md:h-6 mx-auto" />
|
<Logo className="w-auto h-8 md:h-6 mx-auto" />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -38,9 +38,9 @@ const FormField = ({ icons = [], ...props }: Props) => {
|
|||||||
`${focusRing}`,
|
`${focusRing}`,
|
||||||
props.hasTextareaHeight ? 'h-24' : 'h-12',
|
props.hasTextareaHeight ? 'h-24' : 'h-12',
|
||||||
props.isBorderless ? 'border-0' : 'border',
|
props.isBorderless ? 'border-0' : 'border',
|
||||||
props.isTransparent ? 'bg-transparent' : `${props.websiteBg ? ` bg-white` : bgColor} dark:bg-dark-800`,
|
props.isTransparent ? 'bg-transparent' : `${props.websiteBg ? `${bgWebsiteColor} ` : bgColor} dark:bg-dark-800`,
|
||||||
props.disabled ? 'bg-gray-200 text-gray-100 dark:bg-dark-900 disabled' : '',
|
props.disabled ? 'bg-gray-200 text-gray-100 dark:bg-dark-900 disabled' : '',
|
||||||
props.borderButtom ? `border-0 border-b ${props.diversity ? "border-gray-400" : " placeholder-white border-gray-300/10 border-white "} rounded-none focus:ring-0` : '',
|
props.borderButtom ? `border-0 border-b ${props.diversity ? "placeholder-primaryText border-primaryText" : " placeholder-white border-white "} rounded-none focus:ring-0` : '',
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -77,7 +77,7 @@ const FormImagePicker = ({ label, icon, accept, color, isRoundIcon, path, schema
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
{showFilename && !loading && (
|
{showFilename && !loading && (
|
||||||
<div className={` ${cornersRight} px-4 py-2 max-w-full flex-grow-0 overflow-x-hidden ${bgColor} dark:bg-slate-800 border-gray-200 dark:border-slate-700 border `}>
|
<div className={` ${cornersRight} px-4 py-2 max-w-full flex-grow-0 overflow-x-hidden ${bgColor} dark:bg-slate-800 border-gray-200 dark:border-slate-700 `}>
|
||||||
<span className='text-ellipsis max-w-full line-clamp-1'>
|
<span className='text-ellipsis max-w-full line-clamp-1'>
|
||||||
{file.name}
|
{file.name}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export default function ImageField({
|
|||||||
className={`rounded-full block h-auto w-full max-w-full bg-gray-100 dark:bg-dark-900 ${imageClassName}`}
|
className={`rounded-full block h-auto w-full max-w-full bg-gray-100 dark:bg-dark-900 ${imageClassName}`}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className={'flex h-full bg-slate-100 dark:bg-dark-900/70'}>
|
<div className={'flex h-full dark:bg-dark-900/70'}>
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
className='text-black dark:text-white'
|
className='text-black dark:text-white'
|
||||||
w='w-full'
|
w='w-full'
|
||||||
|
|||||||
@ -34,7 +34,7 @@ const KanbanCard = ({
|
|||||||
<div
|
<div
|
||||||
ref={drag}
|
ref={drag}
|
||||||
className={
|
className={
|
||||||
`bg-gray-50 dark:bg-dark-800 rounded-md space-y-2 p-4 relative ${isDragging ? 'cursor-grabbing' : 'cursor-grab'}`
|
`bg-pastelEmeraldTheme-cardColor dark:bg-dark-800 rounded-md space-y-2 p-4 relative ${isDragging ? 'cursor-grabbing' : 'cursor-grab'}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className={'flex items-center justify-between'}>
|
<div className={'flex items-center justify-between'}>
|
||||||
|
|||||||
@ -188,7 +188,7 @@ const KanbanColumn = ({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!data?.length && (
|
{!data?.length && (
|
||||||
<p className={'text-center py-8 bg-gray-50 dark:bg-dark-800'}>No data</p>
|
<p className={'text-center py-8 bg-pastelEmeraldTheme-cardColor dark:bg-dark-800'}>No data</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'next-i18next';
|
|
||||||
import Select, { components, SingleValueProps, OptionProps } from 'react-select';
|
import Select, { components, SingleValueProps, OptionProps } from 'react-select';
|
||||||
|
|
||||||
type LanguageOption = { label: string; value: string };
|
type LanguageOption = { label: string; value: string };
|
||||||
@ -24,19 +23,16 @@ const SingleVal = (props: SingleValueProps<LanguageOption, false>) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const LanguageSwitcher: React.FC = () => {
|
const LanguageSwitcher: React.FC = () => {
|
||||||
const { i18n } = useTranslation();
|
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
const [selected, setSelected] = useState<LanguageOption>(LANGS[0]);
|
const [selected, setSelected] = useState<LanguageOption>(LANGS[0]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
setSelected(LANGS.find(l => l.value === i18n.language) ?? LANGS[0]);
|
}, []);
|
||||||
}, [i18n.language]);
|
|
||||||
|
|
||||||
const handleChange = (opt: LanguageOption | null) => {
|
const handleChange = (opt: LanguageOption | null) => {
|
||||||
if (!opt) return;
|
if (!opt) return;
|
||||||
setSelected(opt);
|
setSelected(opt);
|
||||||
i18n.changeLanguage(opt.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!mounted) return null;
|
if (!mounted) return null;
|
||||||
@ -97,4 +93,4 @@ const LanguageSwitcher: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LanguageSwitcher;
|
export default LanguageSwitcher;
|
||||||
|
|||||||
@ -53,7 +53,7 @@ const ListActionsPopover = ({
|
|||||||
size={'small'}
|
size={'small'}
|
||||||
>
|
>
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
className={`text-black dark:text-white ${iconClassName}`}
|
className={`text-primaryText dark:text-white ${iconClassName}`}
|
||||||
w='w-10'
|
w='w-10'
|
||||||
h='h-10'
|
h='h-10'
|
||||||
size={24}
|
size={24}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ const LoadingSpinner = () => {
|
|||||||
className='w-12 h-12 rounded-full absolute border-4 border-solid border-gray-200 dark:border-slate-800'
|
className='w-12 h-12 rounded-full absolute border-4 border-solid border-gray-200 dark:border-slate-800'
|
||||||
></div>
|
></div>
|
||||||
<div
|
<div
|
||||||
className="w-12 h-12 rounded-full animate-spin absolute border-4 border-solid border-blue-500 dark:border-blue-500 border-t-transparent"
|
className="w-12 h-12 rounded-full animate-spin absolute border-4 border-solid border-pastelEmeraldTheme-iconsColor dark:border-blue-500 border-t-transparent"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import React, { ReactNode, useState, useEffect } from 'react'
|
import React, { ReactNode, useState } from 'react'
|
||||||
import { mdiClose, mdiDotsVertical } from '@mdi/js'
|
import { mdiClose, mdiDotsVertical } from '@mdi/js'
|
||||||
import { containerMaxW } from '../config'
|
import { containerMaxW } from '../config'
|
||||||
import BaseIcon from './BaseIcon'
|
import BaseIcon from './BaseIcon'
|
||||||
import NavBarItemPlain from './NavBarItemPlain'
|
import NavBarItemPlain from './NavBarItemPlain'
|
||||||
import NavBarMenuList from './NavBarMenuList'
|
import NavBarMenuList from './NavBarMenuList'
|
||||||
import { MenuNavBarItem } from '../interfaces'
|
import { MenuNavBarItem } from '../interfaces'
|
||||||
import { useAppSelector } from '../stores/hooks';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
menu: MenuNavBarItem[]
|
menu: MenuNavBarItem[]
|
||||||
@ -15,19 +14,6 @@ type Props = {
|
|||||||
|
|
||||||
export default function NavBar({ menu, className = '', children }: Props) {
|
export default function NavBar({ menu, className = '', children }: Props) {
|
||||||
const [isMenuNavBarActive, setIsMenuNavBarActive] = useState(false)
|
const [isMenuNavBarActive, setIsMenuNavBarActive] = useState(false)
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleScroll = () => {
|
|
||||||
const scrolled = window.scrollY > 0;
|
|
||||||
setIsScrolled(scrolled);
|
|
||||||
};
|
|
||||||
window.addEventListener('scroll', handleScroll);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('scroll', handleScroll);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleMenuNavBarToggleClick = () => {
|
const handleMenuNavBarToggleClick = () => {
|
||||||
setIsMenuNavBarActive(!isMenuNavBarActive)
|
setIsMenuNavBarActive(!isMenuNavBarActive)
|
||||||
@ -35,9 +21,9 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
className={`${className} top-0 inset-x-0 fixed ${bgColor} h-14 z-30 transition-position w-screen lg:w-auto dark:bg-dark-800`}
|
className={`${className} top-0 inset-x-0 fixed bg-white h-14 z-30 transition-position w-screen border-b border-gray-200 lg:w-auto dark:bg-dark-800 dark:border-dark-700`}
|
||||||
>
|
>
|
||||||
<div className={`flex lg:items-stretch ${containerMaxW} ${isScrolled && `border-b border-pavitra-400 dark:border-dark-700`}`}>
|
<div className={`flex lg:items-stretch ${containerMaxW}`}>
|
||||||
<div className="flex flex-1 items-stretch h-14">{children}</div>
|
<div className="flex flex-1 items-stretch h-14">{children}</div>
|
||||||
<div className="flex-none items-stretch flex h-14 lg:hidden">
|
<div className="flex-none items-stretch flex h-14 lg:hidden">
|
||||||
<NavBarItemPlain onClick={handleMenuNavBarToggleClick}>
|
<NavBarItemPlain onClick={handleMenuNavBarToggleClick}>
|
||||||
@ -47,7 +33,7 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
|||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
isMenuNavBarActive ? 'block' : 'hidden'
|
isMenuNavBarActive ? 'block' : 'hidden'
|
||||||
} flex items-center max-h-screen-menu overflow-y-auto lg:overflow-visible absolute w-screen top-14 left-0 ${bgColor} shadow-lg lg:w-auto lg:flex lg:static lg:shadow-none dark:bg-dark-800`}
|
} flex items-center max-h-screen-menu overflow-y-auto lg:overflow-visible absolute w-screen top-14 left-0 bg-white lg:w-auto lg:flex lg:static lg:shadow-none dark:bg-dark-800`}
|
||||||
>
|
>
|
||||||
<NavBarMenuList menu={menu} />
|
<NavBarMenuList menu={menu} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -106,7 +106,7 @@ export default function NavBarItem({ item }: Props) {
|
|||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
!isDropdownActive ? 'lg:hidden' : ''
|
!isDropdownActive ? 'lg:hidden' : ''
|
||||||
} text-sm border-b border-gray-100 lg:border lg:bg-white lg:absolute lg:top-full lg:left-0 lg:min-w-full lg:z-20 lg:rounded-lg lg:shadow-lg lg:dark:bg-dark-900 dark:border-dark-700`}
|
} text-sm border-b border-gray-100 lg:border lg:bg-pastelEmeraldTheme-cardColor lg:absolute lg:top-full lg:left-0 lg:min-w-full lg:z-20 lg:rounded-lg lg:shadow-lg lg:dark:bg-dark-900 dark:border-dark-700`}
|
||||||
>
|
>
|
||||||
<ClickOutside onClickOutside={() => setIsDropdownActive(false)} excludedElements={[excludedRef]}>
|
<ClickOutside onClickOutside={() => setIsDropdownActive(false)} excludedElements={[excludedRef]}>
|
||||||
<NavBarMenuList menu={item.menu} />
|
<NavBarMenuList menu={item.menu} />
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
import { useAppSelector } from '../../stores/hooks';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import { Pagination } from '../Pagination';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
notifications: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CardNotifications = ({
|
|
||||||
notifications,
|
|
||||||
loading,
|
|
||||||
onDelete,
|
|
||||||
currentPage,
|
|
||||||
numPages,
|
|
||||||
onPageChange,
|
|
||||||
}: Props) => {
|
|
||||||
const asideScrollbarsStyle = useAppSelector(
|
|
||||||
(state) => state.style.asideScrollbarsStyle,
|
|
||||||
);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
const darkMode = useAppSelector((state) => state.style.darkMode);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_NOTIFICATIONS')
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={'p-4'}>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
<ul
|
|
||||||
role='list'
|
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
|
||||||
>
|
|
||||||
{!loading && notifications.map((item, index) => (
|
|
||||||
<li
|
|
||||||
key={item.id}
|
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
|
||||||
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
|
||||||
|
|
||||||
<Link href={`/notifications/notifications-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
|
||||||
{item.message}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='ml-auto '>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/notifications/notifications-edit/?id=${item.id}`}
|
|
||||||
pathView={`/notifications/notifications-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
{!loading && notifications.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CardNotifications;
|
|
||||||
@ -1,476 +0,0 @@
|
|||||||
import React, { useEffect, useState, useMemo } from 'react'
|
|
||||||
import { createPortal } from 'react-dom';
|
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
|
||||||
import BaseButton from '../BaseButton'
|
|
||||||
import CardBoxModal from '../CardBoxModal'
|
|
||||||
import CardBox from "../CardBox";
|
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/notifications/notificationsSlice'
|
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { Field, Form, Formik } from "formik";
|
|
||||||
import {
|
|
||||||
DataGrid,
|
|
||||||
GridColDef,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import {loadColumns} from "./configureNotificationsCols";
|
|
||||||
import _ from 'lodash';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import {dataGridStyles} from "../../styles";
|
|
||||||
|
|
||||||
|
|
||||||
import ListNotifications from './ListNotifications';
|
|
||||||
|
|
||||||
|
|
||||||
const perPage = 10
|
|
||||||
|
|
||||||
const TableSampleNotifications = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const pagesList = [];
|
|
||||||
const [id, setId] = useState(null);
|
|
||||||
const [currentPage, setCurrentPage] = useState(0);
|
|
||||||
const [filterRequest, setFilterRequest] = React.useState('');
|
|
||||||
const [columns, setColumns] = useState<GridColDef[]>([]);
|
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
|
||||||
const [sortModel, setSortModel] = useState([
|
|
||||||
{
|
|
||||||
field: '',
|
|
||||||
sort: 'desc',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { notifications, loading, count, notify: notificationsNotify, refetch } = useAppSelector((state) => state.notifications)
|
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
|
|
||||||
for (let i = 0; i < numPages; i++) {
|
|
||||||
pagesList.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadData = async (page = currentPage, request = filterRequest) => {
|
|
||||||
if (page !== currentPage) setCurrentPage(page);
|
|
||||||
if (request !== filterRequest) setFilterRequest(request);
|
|
||||||
const { sort, field } = sortModel[0];
|
|
||||||
|
|
||||||
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
|
|
||||||
dispatch(fetch({ limit: perPage, page, query }));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (notificationsNotify.showNotification) {
|
|
||||||
notify(notificationsNotify.typeNotification, notificationsNotify.textNotification);
|
|
||||||
}
|
|
||||||
}, [notificationsNotify.showNotification]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
loadData();
|
|
||||||
}, [sortModel, currentUser]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (refetch) {
|
|
||||||
loadData(0);
|
|
||||||
dispatch(setRefetch(false));
|
|
||||||
}
|
|
||||||
}, [refetch, dispatch]);
|
|
||||||
|
|
||||||
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
|
|
||||||
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
|
|
||||||
|
|
||||||
const handleModalAction = () => {
|
|
||||||
setIsModalInfoActive(false)
|
|
||||||
setIsModalTrashActive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleDeleteModalAction = (id: string) => {
|
|
||||||
setId(id)
|
|
||||||
setIsModalTrashActive(true)
|
|
||||||
}
|
|
||||||
const handleDeleteAction = async () => {
|
|
||||||
if (id) {
|
|
||||||
await dispatch(deleteItem(id));
|
|
||||||
await loadData(0);
|
|
||||||
setIsModalTrashActive(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateFilterRequests = useMemo(() => {
|
|
||||||
let request = '&';
|
|
||||||
filterItems.forEach((item) => {
|
|
||||||
const isRangeFilter = filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title === item.fields.selectedField &&
|
|
||||||
(filter.number || filter.date),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isRangeFilter) {
|
|
||||||
const from = item.fields.filterValueFrom;
|
|
||||||
const to = item.fields.filterValueTo;
|
|
||||||
if (from) {
|
|
||||||
request += `${item.fields.selectedField}Range=${from}&`;
|
|
||||||
}
|
|
||||||
if (to) {
|
|
||||||
request += `${item.fields.selectedField}Range=${to}&`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const value = item.fields.filterValue;
|
|
||||||
if (value) {
|
|
||||||
request += `${item.fields.selectedField}=${value}&`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return request;
|
|
||||||
}, [filterItems, filters]);
|
|
||||||
|
|
||||||
const deleteFilter = (value) => {
|
|
||||||
const newItems = filterItems.filter((item) => item.id !== value);
|
|
||||||
|
|
||||||
if (newItems.length) {
|
|
||||||
setFilterItems(newItems);
|
|
||||||
} else {
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
setFilterItems(newItems);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
loadData(0, generateFilterRequests);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (id) => (e) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
const name = e.target.name;
|
|
||||||
|
|
||||||
setFilterItems(
|
|
||||||
filterItems.map((item) => {
|
|
||||||
if (item.id !== id) return item;
|
|
||||||
if (name === 'selectedField') return { id, fields: { [name]: value } };
|
|
||||||
|
|
||||||
return { id, fields: { ...item.fields, [name]: value } }
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setFilterItems([]);
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPageChange = (page: number) => {
|
|
||||||
loadData(page);
|
|
||||||
setCurrentPage(page);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
|
|
||||||
loadColumns(
|
|
||||||
handleDeleteModalAction,
|
|
||||||
`notifications`,
|
|
||||||
currentUser,
|
|
||||||
).then((newCols) => setColumns(newCols));
|
|
||||||
}, [currentUser]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleTableSubmit = async (id: string, data) => {
|
|
||||||
|
|
||||||
if (!_.isEmpty(data)) {
|
|
||||||
await dispatch(update({ id, data }))
|
|
||||||
.unwrap()
|
|
||||||
.then((res) => res)
|
|
||||||
.catch((err) => {
|
|
||||||
throw new Error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteRows = async (selectedRows) => {
|
|
||||||
await dispatch(deleteItemsByIds(selectedRows));
|
|
||||||
await loadData(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
const controlClasses =
|
|
||||||
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
|
|
||||||
` ${bgColor} ${focusRing} ${corners} ` +
|
|
||||||
'dark:bg-slate-800 border';
|
|
||||||
|
|
||||||
|
|
||||||
const dataGrid = (
|
|
||||||
<div className='relative overflow-x-auto'>
|
|
||||||
<DataGrid
|
|
||||||
autoHeight
|
|
||||||
rowHeight={64}
|
|
||||||
sx={dataGridStyles}
|
|
||||||
className={'datagrid--table'}
|
|
||||||
getRowClassName={() => `datagrid--row`}
|
|
||||||
rows={notifications ?? []}
|
|
||||||
columns={columns}
|
|
||||||
initialState={{
|
|
||||||
pagination: {
|
|
||||||
paginationModel: {
|
|
||||||
pageSize: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
disableRowSelectionOnClick
|
|
||||||
onProcessRowUpdateError={(params) => {
|
|
||||||
console.log('Error', params);
|
|
||||||
}}
|
|
||||||
processRowUpdate={async (newRow, oldRow) => {
|
|
||||||
const data = dataFormatter.dataGridEditFormatter(newRow);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await handleTableSubmit(newRow.id, data);
|
|
||||||
return newRow;
|
|
||||||
} catch {
|
|
||||||
return oldRow;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
sortingMode={'server'}
|
|
||||||
checkboxSelection
|
|
||||||
onRowSelectionModelChange={(ids) => {
|
|
||||||
setSelectedRows(ids)
|
|
||||||
}}
|
|
||||||
onSortModelChange={(params) => {
|
|
||||||
params.length
|
|
||||||
? setSortModel(params)
|
|
||||||
: setSortModel([{ field: '', sort: 'desc' }]);
|
|
||||||
}}
|
|
||||||
rowCount={count}
|
|
||||||
pageSizeOptions={[10]}
|
|
||||||
paginationMode={'server'}
|
|
||||||
loading={loading}
|
|
||||||
onPaginationModelChange={(params) => {
|
|
||||||
onPageChange(params.page);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
|
|
||||||
<CardBox>
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
checkboxes: ['lorem'],
|
|
||||||
switches: ['lorem'],
|
|
||||||
radio: 'lorem',
|
|
||||||
}}
|
|
||||||
onSubmit={() => null}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<>
|
|
||||||
{filterItems && filterItems.map((filterItem) => {
|
|
||||||
return (
|
|
||||||
<div key={filterItem.id} className="flex mb-4">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Filter</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='selectedField'
|
|
||||||
id='selectedField'
|
|
||||||
component='select'
|
|
||||||
value={filterItem?.fields?.selectedField || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
{filters.map((selectOption) => (
|
|
||||||
<option
|
|
||||||
key={selectOption.title}
|
|
||||||
value={`${selectOption.title}`}
|
|
||||||
>
|
|
||||||
{selectOption.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.type === 'enum' ? (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className="text-gray-500 font-bold">
|
|
||||||
Value
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name="filterValue"
|
|
||||||
id='filterValue'
|
|
||||||
component="select"
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
<option value="">Select Value</option>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.options?.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
) : filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.number ? (
|
|
||||||
<div className="flex flex-row w-full mr-3">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">From</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col w-full">
|
|
||||||
<div className=" text-gray-500 font-bold">To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title ===
|
|
||||||
filterItem?.fields?.selectedField
|
|
||||||
)?.date ? (
|
|
||||||
<div className='flex flex-row w-full mr-3'>
|
|
||||||
<div className='flex flex-col w-full mr-3'>
|
|
||||||
<div className=' text-gray-500 font-bold'>
|
|
||||||
From
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='flex flex-col w-full'>
|
|
||||||
<div className=' text-gray-500 font-bold'>To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Contains</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValue'
|
|
||||||
placeholder='Contained'
|
|
||||||
id='filterValue'
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className=" text-gray-500 font-bold">Action</div>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
type='reset'
|
|
||||||
color='danger'
|
|
||||||
label='Delete'
|
|
||||||
onClick={() => {
|
|
||||||
deleteFilter(filterItem.id)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<div className="flex">
|
|
||||||
<BaseButton
|
|
||||||
className="my-2 mr-3"
|
|
||||||
color="success"
|
|
||||||
label='Apply'
|
|
||||||
onClick={handleSubmit}
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
color='info'
|
|
||||||
label='Cancel'
|
|
||||||
onClick={handleReset}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</CardBox> : null
|
|
||||||
}
|
|
||||||
<CardBoxModal
|
|
||||||
title="Please confirm"
|
|
||||||
buttonColor="info"
|
|
||||||
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
|
|
||||||
isActive={isModalTrashActive}
|
|
||||||
onConfirm={handleDeleteAction}
|
|
||||||
onCancel={handleModalAction}
|
|
||||||
>
|
|
||||||
<p>Are you sure you want to delete this item?</p>
|
|
||||||
</CardBoxModal>
|
|
||||||
|
|
||||||
|
|
||||||
{notifications && Array.isArray(notifications) && !showGrid && (
|
|
||||||
<ListNotifications
|
|
||||||
notifications={notifications}
|
|
||||||
loading={loading}
|
|
||||||
onDelete={handleDeleteModalAction}
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
onPageChange={onPageChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{showGrid && dataGrid}
|
|
||||||
|
|
||||||
|
|
||||||
{selectedRows.length > 0 &&
|
|
||||||
createPortal(
|
|
||||||
<BaseButton
|
|
||||||
className='me-4'
|
|
||||||
color='danger'
|
|
||||||
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
|
|
||||||
onClick={() => onDeleteRows(selectedRows)}
|
|
||||||
/>,
|
|
||||||
document.getElementById('delete-rows-button'),
|
|
||||||
)}
|
|
||||||
<ToastContainer />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TableSampleNotifications
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseIcon from '../BaseIcon';
|
|
||||||
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
|
|
||||||
import axios from 'axios';
|
|
||||||
import {
|
|
||||||
GridActionsCellItem,
|
|
||||||
GridRowParams,
|
|
||||||
GridValueGetterParams,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import DataGridMultiSelect from "../DataGridMultiSelect";
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
type Params = (id: string) => void;
|
|
||||||
|
|
||||||
export const loadColumns = async (
|
|
||||||
onDelete: Params,
|
|
||||||
entityName: string,
|
|
||||||
|
|
||||||
user
|
|
||||||
|
|
||||||
) => {
|
|
||||||
async function callOptionsApi(entityName: string) {
|
|
||||||
|
|
||||||
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await axios(`/${entityName}/autocomplete?limit=100`);
|
|
||||||
return data.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_NOTIFICATIONS')
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'actions',
|
|
||||||
type: 'actions',
|
|
||||||
minWidth: 30,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
getActions: (params: GridRowParams) => {
|
|
||||||
|
|
||||||
return [
|
|
||||||
<div key={params?.row?.id}>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={params?.row?.id}
|
|
||||||
pathEdit={`/notifications/notifications-edit/?id=${params?.row?.id}`}
|
|
||||||
pathView={`/notifications/notifications-view/?id=${params?.row?.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
@ -37,7 +37,7 @@ const ListPermissions = ({ permissions, loading, onDelete, currentPage, numPages
|
|||||||
{!loading && permissions.map((item) => (
|
{!loading && permissions.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex ${bgColor} ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/permissions/permissions-view/?id=${item.id}`}
|
href={`/permissions/permissions-view/?id=${item.id}`}
|
||||||
|
|||||||
@ -412,13 +412,13 @@ const TableSamplePermissions = ({ filterItems, setFilterItems, filters, showGrid
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2 mr-3"
|
className="my-2 mr-3"
|
||||||
color="success"
|
type='submit' color='info'
|
||||||
label='Apply'
|
label='Apply'
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
/>
|
/>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2"
|
className="my-2"
|
||||||
color='info'
|
type='reset' color='info' outline
|
||||||
label='Cancel'
|
label='Cancel'
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -37,7 +37,7 @@ const ListProjects = ({ projects, loading, onDelete, currentPage, numPages, onPa
|
|||||||
{!loading && projects.map((item) => (
|
{!loading && projects.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex ${bgColor} ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/projects/projects-view/?id=${item.id}`}
|
href={`/projects/projects-view/?id=${item.id}`}
|
||||||
|
|||||||
@ -421,13 +421,13 @@ const TableSampleProjects = ({ filterItems, setFilterItems, filters, showGrid })
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2 mr-3"
|
className="my-2 mr-3"
|
||||||
color="success"
|
type='submit' color='info'
|
||||||
label='Apply'
|
label='Apply'
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
/>
|
/>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2"
|
className="my-2"
|
||||||
color='info'
|
type='reset' color='info' outline
|
||||||
label='Cancel'
|
label='Cancel'
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
import { useAppSelector } from '../../stores/hooks';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import { Pagination } from '../Pagination';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
reports: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CardReports = ({
|
|
||||||
reports,
|
|
||||||
loading,
|
|
||||||
onDelete,
|
|
||||||
currentPage,
|
|
||||||
numPages,
|
|
||||||
onPageChange,
|
|
||||||
}: Props) => {
|
|
||||||
const asideScrollbarsStyle = useAppSelector(
|
|
||||||
(state) => state.style.asideScrollbarsStyle,
|
|
||||||
);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
const darkMode = useAppSelector((state) => state.style.darkMode);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_REPORTS')
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={'p-4'}>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
<ul
|
|
||||||
role='list'
|
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
|
||||||
>
|
|
||||||
{!loading && reports.map((item, index) => (
|
|
||||||
<li
|
|
||||||
key={item.id}
|
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
|
||||||
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
|
||||||
|
|
||||||
<Link href={`/reports/reports-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
|
||||||
{item.title}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='ml-auto '>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/reports/reports-edit/?id=${item.id}`}
|
|
||||||
pathView={`/reports/reports-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
{!loading && reports.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CardReports;
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import CardBox from '../CardBox';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import ListActionsPopover from "../ListActionsPopover";
|
|
||||||
import {useAppSelector} from "../../stores/hooks";
|
|
||||||
import {Pagination} from "../Pagination";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
reports: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ListReports = ({ reports, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_REPORTS')
|
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
{!loading && reports.map((item) => (
|
|
||||||
<div key={item.id}>
|
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href={`/reports/reports-view/?id=${item.id}`}
|
|
||||||
className={
|
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
|
|
||||||
</Link>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/reports/reports-edit/?id=${item.id}`}
|
|
||||||
pathView={`/reports/reports-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</CardBox>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{!loading && reports.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ListReports
|
|
||||||
@ -1,463 +0,0 @@
|
|||||||
import React, { useEffect, useState, useMemo } from 'react'
|
|
||||||
import { createPortal } from 'react-dom';
|
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
|
||||||
import BaseButton from '../BaseButton'
|
|
||||||
import CardBoxModal from '../CardBoxModal'
|
|
||||||
import CardBox from "../CardBox";
|
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/reports/reportsSlice'
|
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { Field, Form, Formik } from "formik";
|
|
||||||
import {
|
|
||||||
DataGrid,
|
|
||||||
GridColDef,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import {loadColumns} from "./configureReportsCols";
|
|
||||||
import _ from 'lodash';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import {dataGridStyles} from "../../styles";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const perPage = 10
|
|
||||||
|
|
||||||
const TableSampleReports = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const pagesList = [];
|
|
||||||
const [id, setId] = useState(null);
|
|
||||||
const [currentPage, setCurrentPage] = useState(0);
|
|
||||||
const [filterRequest, setFilterRequest] = React.useState('');
|
|
||||||
const [columns, setColumns] = useState<GridColDef[]>([]);
|
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
|
||||||
const [sortModel, setSortModel] = useState([
|
|
||||||
{
|
|
||||||
field: '',
|
|
||||||
sort: 'desc',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { reports, loading, count, notify: reportsNotify, refetch } = useAppSelector((state) => state.reports)
|
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
|
|
||||||
for (let i = 0; i < numPages; i++) {
|
|
||||||
pagesList.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadData = async (page = currentPage, request = filterRequest) => {
|
|
||||||
if (page !== currentPage) setCurrentPage(page);
|
|
||||||
if (request !== filterRequest) setFilterRequest(request);
|
|
||||||
const { sort, field } = sortModel[0];
|
|
||||||
|
|
||||||
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
|
|
||||||
dispatch(fetch({ limit: perPage, page, query }));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (reportsNotify.showNotification) {
|
|
||||||
notify(reportsNotify.typeNotification, reportsNotify.textNotification);
|
|
||||||
}
|
|
||||||
}, [reportsNotify.showNotification]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
loadData();
|
|
||||||
}, [sortModel, currentUser]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (refetch) {
|
|
||||||
loadData(0);
|
|
||||||
dispatch(setRefetch(false));
|
|
||||||
}
|
|
||||||
}, [refetch, dispatch]);
|
|
||||||
|
|
||||||
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
|
|
||||||
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
|
|
||||||
|
|
||||||
const handleModalAction = () => {
|
|
||||||
setIsModalInfoActive(false)
|
|
||||||
setIsModalTrashActive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleDeleteModalAction = (id: string) => {
|
|
||||||
setId(id)
|
|
||||||
setIsModalTrashActive(true)
|
|
||||||
}
|
|
||||||
const handleDeleteAction = async () => {
|
|
||||||
if (id) {
|
|
||||||
await dispatch(deleteItem(id));
|
|
||||||
await loadData(0);
|
|
||||||
setIsModalTrashActive(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateFilterRequests = useMemo(() => {
|
|
||||||
let request = '&';
|
|
||||||
filterItems.forEach((item) => {
|
|
||||||
const isRangeFilter = filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title === item.fields.selectedField &&
|
|
||||||
(filter.number || filter.date),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isRangeFilter) {
|
|
||||||
const from = item.fields.filterValueFrom;
|
|
||||||
const to = item.fields.filterValueTo;
|
|
||||||
if (from) {
|
|
||||||
request += `${item.fields.selectedField}Range=${from}&`;
|
|
||||||
}
|
|
||||||
if (to) {
|
|
||||||
request += `${item.fields.selectedField}Range=${to}&`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const value = item.fields.filterValue;
|
|
||||||
if (value) {
|
|
||||||
request += `${item.fields.selectedField}=${value}&`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return request;
|
|
||||||
}, [filterItems, filters]);
|
|
||||||
|
|
||||||
const deleteFilter = (value) => {
|
|
||||||
const newItems = filterItems.filter((item) => item.id !== value);
|
|
||||||
|
|
||||||
if (newItems.length) {
|
|
||||||
setFilterItems(newItems);
|
|
||||||
} else {
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
setFilterItems(newItems);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
loadData(0, generateFilterRequests);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (id) => (e) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
const name = e.target.name;
|
|
||||||
|
|
||||||
setFilterItems(
|
|
||||||
filterItems.map((item) => {
|
|
||||||
if (item.id !== id) return item;
|
|
||||||
if (name === 'selectedField') return { id, fields: { [name]: value } };
|
|
||||||
|
|
||||||
return { id, fields: { ...item.fields, [name]: value } }
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setFilterItems([]);
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPageChange = (page: number) => {
|
|
||||||
loadData(page);
|
|
||||||
setCurrentPage(page);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
|
|
||||||
loadColumns(
|
|
||||||
handleDeleteModalAction,
|
|
||||||
`reports`,
|
|
||||||
currentUser,
|
|
||||||
).then((newCols) => setColumns(newCols));
|
|
||||||
}, [currentUser]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleTableSubmit = async (id: string, data) => {
|
|
||||||
|
|
||||||
if (!_.isEmpty(data)) {
|
|
||||||
await dispatch(update({ id, data }))
|
|
||||||
.unwrap()
|
|
||||||
.then((res) => res)
|
|
||||||
.catch((err) => {
|
|
||||||
throw new Error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteRows = async (selectedRows) => {
|
|
||||||
await dispatch(deleteItemsByIds(selectedRows));
|
|
||||||
await loadData(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
const controlClasses =
|
|
||||||
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
|
|
||||||
` ${bgColor} ${focusRing} ${corners} ` +
|
|
||||||
'dark:bg-slate-800 border';
|
|
||||||
|
|
||||||
|
|
||||||
const dataGrid = (
|
|
||||||
<div className='relative overflow-x-auto'>
|
|
||||||
<DataGrid
|
|
||||||
autoHeight
|
|
||||||
rowHeight={64}
|
|
||||||
sx={dataGridStyles}
|
|
||||||
className={'datagrid--table'}
|
|
||||||
getRowClassName={() => `datagrid--row`}
|
|
||||||
rows={reports ?? []}
|
|
||||||
columns={columns}
|
|
||||||
initialState={{
|
|
||||||
pagination: {
|
|
||||||
paginationModel: {
|
|
||||||
pageSize: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
disableRowSelectionOnClick
|
|
||||||
onProcessRowUpdateError={(params) => {
|
|
||||||
console.log('Error', params);
|
|
||||||
}}
|
|
||||||
processRowUpdate={async (newRow, oldRow) => {
|
|
||||||
const data = dataFormatter.dataGridEditFormatter(newRow);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await handleTableSubmit(newRow.id, data);
|
|
||||||
return newRow;
|
|
||||||
} catch {
|
|
||||||
return oldRow;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
sortingMode={'server'}
|
|
||||||
checkboxSelection
|
|
||||||
onRowSelectionModelChange={(ids) => {
|
|
||||||
setSelectedRows(ids)
|
|
||||||
}}
|
|
||||||
onSortModelChange={(params) => {
|
|
||||||
params.length
|
|
||||||
? setSortModel(params)
|
|
||||||
: setSortModel([{ field: '', sort: 'desc' }]);
|
|
||||||
}}
|
|
||||||
rowCount={count}
|
|
||||||
pageSizeOptions={[10]}
|
|
||||||
paginationMode={'server'}
|
|
||||||
loading={loading}
|
|
||||||
onPaginationModelChange={(params) => {
|
|
||||||
onPageChange(params.page);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
|
|
||||||
<CardBox>
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
checkboxes: ['lorem'],
|
|
||||||
switches: ['lorem'],
|
|
||||||
radio: 'lorem',
|
|
||||||
}}
|
|
||||||
onSubmit={() => null}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<>
|
|
||||||
{filterItems && filterItems.map((filterItem) => {
|
|
||||||
return (
|
|
||||||
<div key={filterItem.id} className="flex mb-4">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Filter</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='selectedField'
|
|
||||||
id='selectedField'
|
|
||||||
component='select'
|
|
||||||
value={filterItem?.fields?.selectedField || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
{filters.map((selectOption) => (
|
|
||||||
<option
|
|
||||||
key={selectOption.title}
|
|
||||||
value={`${selectOption.title}`}
|
|
||||||
>
|
|
||||||
{selectOption.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.type === 'enum' ? (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className="text-gray-500 font-bold">
|
|
||||||
Value
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name="filterValue"
|
|
||||||
id='filterValue'
|
|
||||||
component="select"
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
<option value="">Select Value</option>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.options?.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
) : filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.number ? (
|
|
||||||
<div className="flex flex-row w-full mr-3">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">From</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col w-full">
|
|
||||||
<div className=" text-gray-500 font-bold">To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title ===
|
|
||||||
filterItem?.fields?.selectedField
|
|
||||||
)?.date ? (
|
|
||||||
<div className='flex flex-row w-full mr-3'>
|
|
||||||
<div className='flex flex-col w-full mr-3'>
|
|
||||||
<div className=' text-gray-500 font-bold'>
|
|
||||||
From
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='flex flex-col w-full'>
|
|
||||||
<div className=' text-gray-500 font-bold'>To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Contains</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValue'
|
|
||||||
placeholder='Contained'
|
|
||||||
id='filterValue'
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className=" text-gray-500 font-bold">Action</div>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
type='reset'
|
|
||||||
color='danger'
|
|
||||||
label='Delete'
|
|
||||||
onClick={() => {
|
|
||||||
deleteFilter(filterItem.id)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<div className="flex">
|
|
||||||
<BaseButton
|
|
||||||
className="my-2 mr-3"
|
|
||||||
color="success"
|
|
||||||
label='Apply'
|
|
||||||
onClick={handleSubmit}
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
color='info'
|
|
||||||
label='Cancel'
|
|
||||||
onClick={handleReset}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</CardBox> : null
|
|
||||||
}
|
|
||||||
<CardBoxModal
|
|
||||||
title="Please confirm"
|
|
||||||
buttonColor="info"
|
|
||||||
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
|
|
||||||
isActive={isModalTrashActive}
|
|
||||||
onConfirm={handleDeleteAction}
|
|
||||||
onCancel={handleModalAction}
|
|
||||||
>
|
|
||||||
<p>Are you sure you want to delete this item?</p>
|
|
||||||
</CardBoxModal>
|
|
||||||
|
|
||||||
|
|
||||||
{dataGrid}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{selectedRows.length > 0 &&
|
|
||||||
createPortal(
|
|
||||||
<BaseButton
|
|
||||||
className='me-4'
|
|
||||||
color='danger'
|
|
||||||
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
|
|
||||||
onClick={() => onDeleteRows(selectedRows)}
|
|
||||||
/>,
|
|
||||||
document.getElementById('delete-rows-button'),
|
|
||||||
)}
|
|
||||||
<ToastContainer />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TableSampleReports
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import BaseIcon from '../BaseIcon';
|
|
||||||
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
|
|
||||||
import axios from 'axios';
|
|
||||||
import {
|
|
||||||
GridActionsCellItem,
|
|
||||||
GridRowParams,
|
|
||||||
GridValueGetterParams,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import DataGridMultiSelect from "../DataGridMultiSelect";
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
type Params = (id: string) => void;
|
|
||||||
|
|
||||||
export const loadColumns = async (
|
|
||||||
onDelete: Params,
|
|
||||||
entityName: string,
|
|
||||||
|
|
||||||
user
|
|
||||||
|
|
||||||
) => {
|
|
||||||
async function callOptionsApi(entityName: string) {
|
|
||||||
|
|
||||||
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await axios(`/${entityName}/autocomplete?limit=100`);
|
|
||||||
return data.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasUpdatePermission = hasPermission(user, 'UPDATE_REPORTS')
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'actions',
|
|
||||||
type: 'actions',
|
|
||||||
minWidth: 30,
|
|
||||||
headerClassName: 'datagrid--header',
|
|
||||||
cellClassName: 'datagrid--cell',
|
|
||||||
getActions: (params: GridRowParams) => {
|
|
||||||
|
|
||||||
return [
|
|
||||||
<div key={params?.row?.id}>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={params?.row?.id}
|
|
||||||
pathEdit={`/reports/reports-edit/?id=${params?.row?.id}`}
|
|
||||||
pathView={`/reports/reports-view/?id=${params?.row?.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
@ -37,7 +37,7 @@ const ListRoles = ({ roles, loading, onDelete, currentPage, numPages, onPageChan
|
|||||||
{!loading && roles.map((item) => (
|
{!loading && roles.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex ${bgColor} ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/roles/roles-view/?id=${item.id}`}
|
href={`/roles/roles-view/?id=${item.id}`}
|
||||||
|
|||||||
@ -412,13 +412,13 @@ const TableSampleRoles = ({ filterItems, setFilterItems, filters, showGrid }) =>
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2 mr-3"
|
className="my-2 mr-3"
|
||||||
color="success"
|
type='submit' color='info'
|
||||||
label='Apply'
|
label='Apply'
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
/>
|
/>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2"
|
className="my-2"
|
||||||
color='info'
|
type='reset' color='info' outline
|
||||||
label='Cancel'
|
label='Cancel'
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -2,10 +2,8 @@ import React from 'react';
|
|||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useAppSelector } from '../stores/hooks';
|
import { useAppSelector } from '../stores/hooks';
|
||||||
import { useTranslation } from 'next-i18next';
|
|
||||||
|
|
||||||
const Search = () => {
|
const Search = () => {
|
||||||
const { t } = useTranslation('common');
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
const corners = useAppSelector((state) => state.style.corners);
|
||||||
@ -13,9 +11,9 @@ const Search = () => {
|
|||||||
const validateSearch = (value) => {
|
const validateSearch = (value) => {
|
||||||
let error;
|
let error;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
error = t('components.search.required');
|
error = 'Required';
|
||||||
} else if (value.length < 2) {
|
} else if (value.length < 2) {
|
||||||
error = t('components.search.minLength', { count: 2 });
|
error = 'Minimum length: 2 characters';
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
};
|
};
|
||||||
@ -38,7 +36,7 @@ const Search = () => {
|
|||||||
id='search'
|
id='search'
|
||||||
name='search'
|
name='search'
|
||||||
validate={validateSearch}
|
validate={validateSearch}
|
||||||
placeholder={t('components.search.placeholder', { defaultValue: 'Search' })}
|
placeholder='Search'
|
||||||
className={` ${corners} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-2 relative ml-2 w-full dark:placeholder-dark-600 ${focusRing} shadow-none`}
|
className={` ${corners} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-2 relative ml-2 w-full dark:placeholder-dark-600 ${focusRing} shadow-none`}
|
||||||
/>
|
/>
|
||||||
{errors.search && touched.search && values.search.length < 2 ? (
|
{errors.search && touched.search && values.search.length < 2 ? (
|
||||||
@ -49,4 +47,4 @@ const Search = () => {
|
|||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default Search;
|
export default Search;
|
||||||
|
|||||||
@ -6,5 +6,5 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function SectionMain({ children }: Props) {
|
export default function SectionMain({ children }: Props) {
|
||||||
return <section className={`p-6 ${containerMaxW}`}>{children}</section>
|
return <section className={`p-8 ${containerMaxW}`}>{children}</section>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,9 +17,9 @@ const SectionTitle = ({ custom = false, first = false, last = false, children }:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`py-24 px-6 lg:px-0 lg:max-w-2xl lg:mx-auto text-center ${classAddon}`}>
|
<section className={`py-12 px-6 lg:px-0 lg:max-w-2xl lg:mx-auto text-center ${classAddon}`}>
|
||||||
{custom && children}
|
{custom && children}
|
||||||
{!custom && <h1 className="text-2xl text-gray-500 dark:text-slate-400">{children}</h1>}
|
{!custom && <h1 className="text-2xl text-gray-700 dark:text-gray-300">{children}</h1>}
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
import { mdiCog } from '@mdi/js'
|
import React, { ReactNode } from 'react'
|
||||||
import React, { Children, ReactNode } from 'react'
|
|
||||||
import BaseButton from './BaseButton'
|
|
||||||
import BaseIcon from './BaseIcon'
|
import BaseIcon from './BaseIcon'
|
||||||
import IconRounded from './IconRounded'
|
|
||||||
import { humanize } from '../helpers/humanize';
|
import { humanize } from '../helpers/humanize';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -13,17 +10,13 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function SectionTitleLineWithButton({ icon, title, main = false, children }: Props) {
|
export default function SectionTitleLineWithButton({ icon, title, main = false, children }: Props) {
|
||||||
const hasChildren = !!Children.count(children)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`${main ? '' : 'pt-6'} mb-6 flex items-center justify-between`}>
|
<section className={`${main ? '' : 'pt-6'} mb-6 flex items-center justify-between`}>
|
||||||
<div className="flex items-center justify-start">
|
<div className="flex items-center justify-start">
|
||||||
{icon && main && <IconRounded icon={icon} color="light" className="mr-3" bg />}
|
{icon && <BaseIcon path={icon} className="mr-3" size="24" />}
|
||||||
{icon && !main && <BaseIcon path={icon} className="mr-2" size="20" />}
|
<h1 className={`leading-tight ${main ? 'text-2xl' : 'text-xl'}`}>{humanize(title)}</h1>
|
||||||
<h1 className={`leading-tight ${main ? 'text-3xl' : 'text-2xl'}`}>{humanize(title)}</h1>
|
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
{!hasChildren && <BaseButton icon={mdiCog} color="whiteDark" />}
|
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import React, { useEffect, useId, useState } from 'react';
|
import React, { useEffect, useId, useState } from 'react';
|
||||||
import Switch from "react-switch";
|
import Switch from "react-switch";
|
||||||
|
|
||||||
|
import resolveConfig from 'tailwindcss/resolveConfig'
|
||||||
|
import tailwindConfig from '../../tailwind.config.js';
|
||||||
|
const fullConfig = resolveConfig(tailwindConfig as any);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const SwitchField = ({
|
export const SwitchField = ({
|
||||||
@ -16,7 +20,10 @@ export const SwitchField = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const buttonColor = fullConfig.theme.accentColor.pastelEmeraldTheme?.buttonColor;
|
||||||
|
const cardColor = fullConfig.theme.accentColor.pastelEmeraldTheme?.cardColor;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch checkedIcon={false} uncheckedIcon={false} className={'check'} onChange={handleChange} checked={!!field?.value} disabled={disabled} />
|
<Switch checkedIcon={false} uncheckedIcon={false} className={'check'} onChange={handleChange} onColor={buttonColor} offColor={cardColor} checked={!!field?.value} disabled={disabled} />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -37,7 +37,7 @@ const ListTasks = ({ tasks, loading, onDelete, currentPage, numPages, onPageChan
|
|||||||
{!loading && tasks.map((item) => (
|
{!loading && tasks.map((item) => (
|
||||||
<div key={item.id}>
|
<div key={item.id}>
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
<CardBox hasTable isList className={'rounded shadow-none'}>
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
<div className={`flex ${bgColor} ${corners !== 'rounded-full' ? corners : 'rounded-3xl'} dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/tasks/tasks-view/?id=${item.id}`}
|
href={`/tasks/tasks-view/?id=${item.id}`}
|
||||||
|
|||||||
@ -98,18 +98,16 @@ const TableSampleTasks = ({ filterItems, setFilterItems, filters, showGrid }) =>
|
|||||||
|
|
||||||
setKanbanColumns([
|
setKanbanColumns([
|
||||||
|
|
||||||
{ id: "Backlog", label: "Backlog" },
|
|
||||||
|
|
||||||
{ id: "ToDo", label: "ToDo" },
|
{ id: "ToDo", label: "ToDo" },
|
||||||
|
|
||||||
{ id: "InProgress", label: "InProgress" },
|
{ id: "InProgress", label: "InProgress" },
|
||||||
|
|
||||||
{ id: "InReview", label: "InReview" },
|
{ id: "InReview", label: "InReview" },
|
||||||
|
|
||||||
{ id: "Done", label: "Done" },
|
|
||||||
|
|
||||||
{ id: "Blocked", label: "Blocked" },
|
{ id: "Blocked", label: "Blocked" },
|
||||||
|
|
||||||
|
{ id: "Done", label: "Done" },
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
@ -447,13 +445,13 @@ const TableSampleTasks = ({ filterItems, setFilterItems, filters, showGrid }) =>
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2 mr-3"
|
className="my-2 mr-3"
|
||||||
color="success"
|
type='submit' color='info'
|
||||||
label='Apply'
|
label='Apply'
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
/>
|
/>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className="my-2"
|
className="my-2"
|
||||||
color='info'
|
type='reset' color='info' outline
|
||||||
label='Cancel'
|
label='Cancel'
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import ListActionsPopover from '../ListActionsPopover';
|
|
||||||
import { useAppSelector } from '../../stores/hooks';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import { Pagination } from '../Pagination';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
team_members: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CardTeam_members = ({
|
|
||||||
team_members,
|
|
||||||
loading,
|
|
||||||
onDelete,
|
|
||||||
currentPage,
|
|
||||||
numPages,
|
|
||||||
onPageChange,
|
|
||||||
}: Props) => {
|
|
||||||
const asideScrollbarsStyle = useAppSelector(
|
|
||||||
(state) => state.style.asideScrollbarsStyle,
|
|
||||||
);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
const darkMode = useAppSelector((state) => state.style.darkMode);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_TEAM_MEMBERS')
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={'p-4'}>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
<ul
|
|
||||||
role='list'
|
|
||||||
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
|
|
||||||
>
|
|
||||||
{!loading && team_members.map((item, index) => (
|
|
||||||
<li
|
|
||||||
key={item.id}
|
|
||||||
className={`overflow-hidden ${corners !== 'rounded-full'? corners : 'rounded-3xl'} border ${focusRing} border-gray-200 dark:border-dark-700 ${
|
|
||||||
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
|
|
||||||
<div className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}>
|
|
||||||
|
|
||||||
<Link href={`/team_members/team_members-view/?id=${item.id}`} className='text-lg font-bold leading-6 line-clamp-1'>
|
|
||||||
{item.display_label}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='ml-auto '>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/team_members/team_members-edit/?id=${item.id}`}
|
|
||||||
pathView={`/team_members/team_members-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
{!loading && team_members.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CardTeam_members;
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import CardBox from '../CardBox';
|
|
||||||
import ImageField from '../ImageField';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import {saveFile} from "../../helpers/fileSaver";
|
|
||||||
import ListActionsPopover from "../ListActionsPopover";
|
|
||||||
import {useAppSelector} from "../../stores/hooks";
|
|
||||||
import {Pagination} from "../Pagination";
|
|
||||||
import LoadingSpinner from "../LoadingSpinner";
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {hasPermission} from "../../helpers/userPermissions";
|
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
team_members: any[];
|
|
||||||
loading: boolean;
|
|
||||||
onDelete: (id: string) => void;
|
|
||||||
currentPage: number;
|
|
||||||
numPages: number;
|
|
||||||
onPageChange: (page: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ListTeam_members = ({ team_members, loading, onDelete, currentPage, numPages, onPageChange }: Props) => {
|
|
||||||
|
|
||||||
const currentUser = useAppSelector((state) => state.auth.currentUser);
|
|
||||||
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_TEAM_MEMBERS')
|
|
||||||
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.cardsColor);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className='relative overflow-x-auto p-4 space-y-4'>
|
|
||||||
{loading && <LoadingSpinner />}
|
|
||||||
{!loading && team_members.map((item) => (
|
|
||||||
<div key={item.id}>
|
|
||||||
<CardBox hasTable isList className={'rounded shadow-none'}>
|
|
||||||
<div className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}>
|
|
||||||
|
|
||||||
<Link
|
|
||||||
href={`/team_members/team_members-view/?id=${item.id}`}
|
|
||||||
className={
|
|
||||||
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
|
|
||||||
</Link>
|
|
||||||
<ListActionsPopover
|
|
||||||
onDelete={onDelete}
|
|
||||||
itemId={item.id}
|
|
||||||
pathEdit={`/team_members/team_members-edit/?id=${item.id}`}
|
|
||||||
pathView={`/team_members/team_members-view/?id=${item.id}`}
|
|
||||||
|
|
||||||
hasUpdatePermission={hasUpdatePermission}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</CardBox>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{!loading && team_members.length === 0 && (
|
|
||||||
<div className='col-span-full flex items-center justify-center h-40'>
|
|
||||||
<p className=''>No data to display</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className={'flex items-center justify-center my-6'}>
|
|
||||||
<Pagination
|
|
||||||
currentPage={currentPage}
|
|
||||||
numPages={numPages}
|
|
||||||
setCurrentPage={onPageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ListTeam_members
|
|
||||||
@ -1,463 +0,0 @@
|
|||||||
import React, { useEffect, useState, useMemo } from 'react'
|
|
||||||
import { createPortal } from 'react-dom';
|
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
|
||||||
import BaseButton from '../BaseButton'
|
|
||||||
import CardBoxModal from '../CardBoxModal'
|
|
||||||
import CardBox from "../CardBox";
|
|
||||||
import { fetch, update, deleteItem, setRefetch, deleteItemsByIds } from '../../stores/team_members/team_membersSlice'
|
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { Field, Form, Formik } from "formik";
|
|
||||||
import {
|
|
||||||
DataGrid,
|
|
||||||
GridColDef,
|
|
||||||
} from '@mui/x-data-grid';
|
|
||||||
import {loadColumns} from "./configureTeam_membersCols";
|
|
||||||
import _ from 'lodash';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter'
|
|
||||||
import {dataGridStyles} from "../../styles";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const perPage = 10
|
|
||||||
|
|
||||||
const TableSampleTeam_members = ({ filterItems, setFilterItems, filters, showGrid }) => {
|
|
||||||
const notify = (type, msg) => toast( msg, {type, position: "bottom-center"});
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const pagesList = [];
|
|
||||||
const [id, setId] = useState(null);
|
|
||||||
const [currentPage, setCurrentPage] = useState(0);
|
|
||||||
const [filterRequest, setFilterRequest] = React.useState('');
|
|
||||||
const [columns, setColumns] = useState<GridColDef[]>([]);
|
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
|
||||||
const [sortModel, setSortModel] = useState([
|
|
||||||
{
|
|
||||||
field: '',
|
|
||||||
sort: 'desc',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { team_members, loading, count, notify: team_membersNotify, refetch } = useAppSelector((state) => state.team_members)
|
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
|
||||||
const focusRing = useAppSelector((state) => state.style.focusRingColor);
|
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
|
||||||
const corners = useAppSelector((state) => state.style.corners);
|
|
||||||
const numPages = Math.floor(count / perPage) === 0 ? 1 : Math.ceil(count / perPage);
|
|
||||||
for (let i = 0; i < numPages; i++) {
|
|
||||||
pagesList.push(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadData = async (page = currentPage, request = filterRequest) => {
|
|
||||||
if (page !== currentPage) setCurrentPage(page);
|
|
||||||
if (request !== filterRequest) setFilterRequest(request);
|
|
||||||
const { sort, field } = sortModel[0];
|
|
||||||
|
|
||||||
const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;
|
|
||||||
dispatch(fetch({ limit: perPage, page, query }));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (team_membersNotify.showNotification) {
|
|
||||||
notify(team_membersNotify.typeNotification, team_membersNotify.textNotification);
|
|
||||||
}
|
|
||||||
}, [team_membersNotify.showNotification]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
loadData();
|
|
||||||
}, [sortModel, currentUser]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (refetch) {
|
|
||||||
loadData(0);
|
|
||||||
dispatch(setRefetch(false));
|
|
||||||
}
|
|
||||||
}, [refetch, dispatch]);
|
|
||||||
|
|
||||||
const [isModalInfoActive, setIsModalInfoActive] = useState(false)
|
|
||||||
const [isModalTrashActive, setIsModalTrashActive] = useState(false)
|
|
||||||
|
|
||||||
const handleModalAction = () => {
|
|
||||||
setIsModalInfoActive(false)
|
|
||||||
setIsModalTrashActive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleDeleteModalAction = (id: string) => {
|
|
||||||
setId(id)
|
|
||||||
setIsModalTrashActive(true)
|
|
||||||
}
|
|
||||||
const handleDeleteAction = async () => {
|
|
||||||
if (id) {
|
|
||||||
await dispatch(deleteItem(id));
|
|
||||||
await loadData(0);
|
|
||||||
setIsModalTrashActive(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateFilterRequests = useMemo(() => {
|
|
||||||
let request = '&';
|
|
||||||
filterItems.forEach((item) => {
|
|
||||||
const isRangeFilter = filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title === item.fields.selectedField &&
|
|
||||||
(filter.number || filter.date),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isRangeFilter) {
|
|
||||||
const from = item.fields.filterValueFrom;
|
|
||||||
const to = item.fields.filterValueTo;
|
|
||||||
if (from) {
|
|
||||||
request += `${item.fields.selectedField}Range=${from}&`;
|
|
||||||
}
|
|
||||||
if (to) {
|
|
||||||
request += `${item.fields.selectedField}Range=${to}&`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const value = item.fields.filterValue;
|
|
||||||
if (value) {
|
|
||||||
request += `${item.fields.selectedField}=${value}&`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return request;
|
|
||||||
}, [filterItems, filters]);
|
|
||||||
|
|
||||||
const deleteFilter = (value) => {
|
|
||||||
const newItems = filterItems.filter((item) => item.id !== value);
|
|
||||||
|
|
||||||
if (newItems.length) {
|
|
||||||
setFilterItems(newItems);
|
|
||||||
} else {
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
setFilterItems(newItems);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
loadData(0, generateFilterRequests);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChange = (id) => (e) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
const name = e.target.name;
|
|
||||||
|
|
||||||
setFilterItems(
|
|
||||||
filterItems.map((item) => {
|
|
||||||
if (item.id !== id) return item;
|
|
||||||
if (name === 'selectedField') return { id, fields: { [name]: value } };
|
|
||||||
|
|
||||||
return { id, fields: { ...item.fields, [name]: value } }
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setFilterItems([]);
|
|
||||||
loadData(0, '');
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPageChange = (page: number) => {
|
|
||||||
loadData(page);
|
|
||||||
setCurrentPage(page);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!currentUser) return;
|
|
||||||
|
|
||||||
loadColumns(
|
|
||||||
handleDeleteModalAction,
|
|
||||||
`team_members`,
|
|
||||||
currentUser,
|
|
||||||
).then((newCols) => setColumns(newCols));
|
|
||||||
}, [currentUser]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleTableSubmit = async (id: string, data) => {
|
|
||||||
|
|
||||||
if (!_.isEmpty(data)) {
|
|
||||||
await dispatch(update({ id, data }))
|
|
||||||
.unwrap()
|
|
||||||
.then((res) => res)
|
|
||||||
.catch((err) => {
|
|
||||||
throw new Error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteRows = async (selectedRows) => {
|
|
||||||
await dispatch(deleteItemsByIds(selectedRows));
|
|
||||||
await loadData(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
const controlClasses =
|
|
||||||
'w-full py-2 px-2 my-2 rounded dark:placeholder-gray-400 ' +
|
|
||||||
` ${bgColor} ${focusRing} ${corners} ` +
|
|
||||||
'dark:bg-slate-800 border';
|
|
||||||
|
|
||||||
|
|
||||||
const dataGrid = (
|
|
||||||
<div className='relative overflow-x-auto'>
|
|
||||||
<DataGrid
|
|
||||||
autoHeight
|
|
||||||
rowHeight={64}
|
|
||||||
sx={dataGridStyles}
|
|
||||||
className={'datagrid--table'}
|
|
||||||
getRowClassName={() => `datagrid--row`}
|
|
||||||
rows={team_members ?? []}
|
|
||||||
columns={columns}
|
|
||||||
initialState={{
|
|
||||||
pagination: {
|
|
||||||
paginationModel: {
|
|
||||||
pageSize: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
disableRowSelectionOnClick
|
|
||||||
onProcessRowUpdateError={(params) => {
|
|
||||||
console.log('Error', params);
|
|
||||||
}}
|
|
||||||
processRowUpdate={async (newRow, oldRow) => {
|
|
||||||
const data = dataFormatter.dataGridEditFormatter(newRow);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await handleTableSubmit(newRow.id, data);
|
|
||||||
return newRow;
|
|
||||||
} catch {
|
|
||||||
return oldRow;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
sortingMode={'server'}
|
|
||||||
checkboxSelection
|
|
||||||
onRowSelectionModelChange={(ids) => {
|
|
||||||
setSelectedRows(ids)
|
|
||||||
}}
|
|
||||||
onSortModelChange={(params) => {
|
|
||||||
params.length
|
|
||||||
? setSortModel(params)
|
|
||||||
: setSortModel([{ field: '', sort: 'desc' }]);
|
|
||||||
}}
|
|
||||||
rowCount={count}
|
|
||||||
pageSizeOptions={[10]}
|
|
||||||
paginationMode={'server'}
|
|
||||||
loading={loading}
|
|
||||||
onPaginationModelChange={(params) => {
|
|
||||||
onPageChange(params.page);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{filterItems && Array.isArray( filterItems ) && filterItems.length ?
|
|
||||||
<CardBox>
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
checkboxes: ['lorem'],
|
|
||||||
switches: ['lorem'],
|
|
||||||
radio: 'lorem',
|
|
||||||
}}
|
|
||||||
onSubmit={() => null}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<>
|
|
||||||
{filterItems && filterItems.map((filterItem) => {
|
|
||||||
return (
|
|
||||||
<div key={filterItem.id} className="flex mb-4">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Filter</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='selectedField'
|
|
||||||
id='selectedField'
|
|
||||||
component='select'
|
|
||||||
value={filterItem?.fields?.selectedField || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
{filters.map((selectOption) => (
|
|
||||||
<option
|
|
||||||
key={selectOption.title}
|
|
||||||
value={`${selectOption.title}`}
|
|
||||||
>
|
|
||||||
{selectOption.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.type === 'enum' ? (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className="text-gray-500 font-bold">
|
|
||||||
Value
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name="filterValue"
|
|
||||||
id='filterValue'
|
|
||||||
component="select"
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
>
|
|
||||||
<option value="">Select Value</option>
|
|
||||||
{filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.options?.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
) : filters.find((filter) =>
|
|
||||||
filter.title === filterItem?.fields?.selectedField
|
|
||||||
)?.number ? (
|
|
||||||
<div className="flex flex-row w-full mr-3">
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">From</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col w-full">
|
|
||||||
<div className=" text-gray-500 font-bold">To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : filters.find(
|
|
||||||
(filter) =>
|
|
||||||
filter.title ===
|
|
||||||
filterItem?.fields?.selectedField
|
|
||||||
)?.date ? (
|
|
||||||
<div className='flex flex-row w-full mr-3'>
|
|
||||||
<div className='flex flex-col w-full mr-3'>
|
|
||||||
<div className=' text-gray-500 font-bold'>
|
|
||||||
From
|
|
||||||
</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueFrom'
|
|
||||||
placeholder='From'
|
|
||||||
id='filterValueFrom'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueFrom || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='flex flex-col w-full'>
|
|
||||||
<div className=' text-gray-500 font-bold'>To</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValueTo'
|
|
||||||
placeholder='to'
|
|
||||||
id='filterValueTo'
|
|
||||||
type='datetime-local'
|
|
||||||
value={filterItem?.fields?.filterValueTo || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col w-full mr-3">
|
|
||||||
<div className=" text-gray-500 font-bold">Contains</div>
|
|
||||||
<Field
|
|
||||||
className={controlClasses}
|
|
||||||
name='filterValue'
|
|
||||||
placeholder='Contained'
|
|
||||||
id='filterValue'
|
|
||||||
value={filterItem?.fields?.filterValue || ''}
|
|
||||||
onChange={handleChange(filterItem.id)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className=" text-gray-500 font-bold">Action</div>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
type='reset'
|
|
||||||
color='danger'
|
|
||||||
label='Delete'
|
|
||||||
onClick={() => {
|
|
||||||
deleteFilter(filterItem.id)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<div className="flex">
|
|
||||||
<BaseButton
|
|
||||||
className="my-2 mr-3"
|
|
||||||
color="success"
|
|
||||||
label='Apply'
|
|
||||||
onClick={handleSubmit}
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
className="my-2"
|
|
||||||
color='info'
|
|
||||||
label='Cancel'
|
|
||||||
onClick={handleReset}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</CardBox> : null
|
|
||||||
}
|
|
||||||
<CardBoxModal
|
|
||||||
title="Please confirm"
|
|
||||||
buttonColor="info"
|
|
||||||
buttonLabel={loading ? 'Deleting...' : 'Confirm'}
|
|
||||||
isActive={isModalTrashActive}
|
|
||||||
onConfirm={handleDeleteAction}
|
|
||||||
onCancel={handleModalAction}
|
|
||||||
>
|
|
||||||
<p>Are you sure you want to delete this item?</p>
|
|
||||||
</CardBoxModal>
|
|
||||||
|
|
||||||
|
|
||||||
{dataGrid}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{selectedRows.length > 0 &&
|
|
||||||
createPortal(
|
|
||||||
<BaseButton
|
|
||||||
className='me-4'
|
|
||||||
color='danger'
|
|
||||||
label={`Delete ${selectedRows.length === 1 ? 'Row' : 'Rows'}`}
|
|
||||||
onClick={() => onDeleteRows(selectedRows)}
|
|
||||||
/>,
|
|
||||||
document.getElementById('delete-rows-button'),
|
|
||||||
)}
|
|
||||||
<ToastContainer />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TableSampleTeam_members
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user