diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..0643ee0
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,9 @@
+{
+ "extends": [
+ "next/core-web-vitals",
+ "plugin:security/recommended-legacy"
+ ],
+ "plugins": [
+ "security"
+ ]
+}
\ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..05a93e5
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,18 @@
+version: 2
+updates:
+ - package-ecosystem: "npm"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 5
+ groups:
+ minor-and-patch:
+ update-types: ["minor", "patch"]
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
+
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
\ No newline at end of file
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 0000000..3709458
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,62 @@
+name: CI
+
+on:
+ pull_request:
+ branches: [main]
+ push:
+ branches: [main]
+
+permissions:
+ contents: read
+
+concurrency:
+ group: ci-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ quality:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: npm
+
+ - name: Install Dependencies
+ run: npm ci
+
+ - name: Lint
+ run: npm run lint
+
+ - name: Type Check
+ run: npm run typecheck
+ continue-on-error: true
+
+ - name: Write Format
+ run: npm run format:write
+
+ - name: Check Format
+ run: npm run format:check
+
+ - name: Build
+ run: npx vercel build --yes
+ env:
+ VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
+ VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
+ MONGODB_URI: ${{ secrets.MONGODB_URI }}
+ NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
+ NEXT_PUBLIC_SALESFAM_API_KEY: ${{ secrets.NEXT_PUBLIC_SALESFAM_API_KEY }}
+ RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
+
+ secrets-scan:
+ runs-on: ubuntu-latest
+ container: zricethezav/gitleaks:latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Escanear secretos
+ run: gitleaks detect --source=. --verbose --redact
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0523389
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+.env
+.env*.local
+node_modules/
+.next/
+out/
+coverage/
+.vercel
+*.tsbuildinfo
+npm-debug.log*
+.DS_Store
diff --git a/SECURITY_DEBT.md b/SECURITY_DEBT.md
new file mode 100644
index 0000000..a23db08
--- /dev/null
+++ b/SECURITY_DEBT.md
@@ -0,0 +1,24 @@
+# Known Security Debt
+
+## 🔴 Pending — Requires Developer Fix
+
+### 1. Regex Injection / ReDoS in Company Search
+- **File:** `app/api/company/route.js:120`
+- **Risk:** `companyName` is received unsanitized from a query parameter and concatenated into a `RegExp`. This allows for ReDoS (server hang) and exact match bypass.
+- **Suggested Fix:** Escape special regex characters before building the `RegExp`, or use an exact Mongo filter instead of `$regex`.
+- **Detected by:** `eslint-plugin-security` (`detect-non-literal-regexp`), does not block CI (severity: warning).
+
+### 2. List Elements Without `key` (5 instances)
+- **Files:** `app/settings/manage-contracts/AddContract.tsx:144`, `EditContract.tsx:156`, `components/ContractTable.jsx:99`, `components/datatable.tsx:363`, `components/datatableSeller1.tsx:275`
+- **Risk:** Not a security issue — it is a React bug that can cause incorrect rendering when reordering/updating lists.
+- **Suggested Fix:** Add `key={unique-id}` to each iterated element.
+- **Note:** Temporarily downgraded to a warning via `eslint-disable-next-line` to avoid blocking the pipeline — see comments in each file.
+
+## 🟢 Reviewed — False Positive, No Action Required
+
+- `components/ResetPassword.tsx:29`, `components/SetPassword.tsx:34` — Comparison between two fields of the same form, not between a secret and a stored value. No remote attacker can measure timing here.
+- `components/TabComponent.tsx:23` — The index used never comes from user input, only from the `tabs` array itself rendered by the component.
+
+## Recommended run 'npm run lint' to see other needs
+
+- `grep -rn "eslint-disable" --include="*.tsx" --include="*.jsx" --include="*.js" .` run it eventually to check not documented bypassed lines.
\ No newline at end of file
diff --git a/app/add-project/AddProjectClient.tsx b/app/add-project/AddProjectClient.tsx
new file mode 100644
index 0000000..78dcf6b
--- /dev/null
+++ b/app/add-project/AddProjectClient.tsx
@@ -0,0 +1,640 @@
+"use client"
+
+import { useEffect, useState } from "react"
+import * as React from "react"
+import Link from "next/link"
+import { useRouter } from "next/navigation"
+import { format } from "date-fns"
+import {
+ ArrowLeftIcon,
+ Calendar as CalendarIcon,
+ Check,
+ ChevronsUpDown,
+} from "lucide-react"
+import { useSession } from "next-auth/react"
+
+import { fetchCompanies } from "@/lib/company/company"
+import { fetchClients } from "@/lib/fetchClients"
+import { getUser } from "@/lib/getUser"
+import {
+ sendAdminNotification,
+ sendUserNotification,
+} from "@/lib/notification/sendNotification"
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import { Calendar } from "@/components/ui/calendar"
+import { Checkbox } from "@/components/ui/checkbox"
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+} from "@/components/ui/command"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+import { Textarea } from "@/components/ui/textarea"
+import { useToast } from "@/components/ui/use-toast"
+import { Icons } from "@/components/icons"
+
+export default function AddProject() {
+ const AdminEmail = process.env.NEXT_PUBLIC_ADMIN_EMAIL
+ const [open, setOpen] = useState(false)
+ const [openClient, setOpenClient] = useState(false)
+ const [value, setValue] = useState("")
+ const [valueClient, setValueClient] = useState("")
+
+ const { toast } = useToast()
+ const router = useRouter()
+
+ const { data: session } = useSession()
+
+ const [isLoading, setLoading] = useState(false)
+ const [projectName, setprojectName] = useState("")
+ const [salesPerson, setUserId] = useState("")
+ const [salesPersonEmail, setSalesPersonEmail] = useState("")
+ const [salesId, setsalesId] = useState()
+ const [clientId, setClientId] = useState("")
+ const [upSellerId, setupSellerId] = useState()
+ const [projectDetails, setprojectDetails] = useState("")
+ const [budget, setbudget] = useState("")
+ const [companyName, setcompanyName] = useState("image appeal")
+ let [dateSigned, setdateSigned] = useState("")
+ const [clientName, setclientName] = useState("")
+ const [email, setemail] = useState("")
+ const [phone, setphone] = useState("")
+ const [address, setaddress] = useState("")
+ const [Users, setUsers] = useState([])
+ const [clients, setClients] = useState([])
+ const [Allcompanies, setAllcompanies] = useState()
+ const [contracts, setContracts] = useState()
+ const [currentUser, setcurrentUser] = useState()
+ const [isClientCall, setIsClientCall] = useState(false)
+ const [isClientEmail, setIsClientEmail] = useState(false)
+ const [hasClient, setHasClient] = useState(false)
+ const role = session?.user?.role
+ const name = session?.user?.name
+ const id = session?.user?.id
+ const salesPersonsEmail = session?.user?.email
+ console.log(salesPersonsEmail)
+
+ useEffect(() => {
+ fetch("/api/user")
+ .then((response) => response.json())
+ .then((data) => {
+ const main = data.users
+ const users = main.filter((item) => item.role != "SuperAdmin")
+ setUsers(users)
+ console.log(users)
+ })
+ .catch((error) => {
+ console.error("Error:", error)
+ })
+
+ if (role != "SuperAdmin") {
+ setUserId(name)
+
+
+ }
+
+ fetchCompanies().then((data) => {
+ setAllcompanies(data)
+ })
+
+ if (session) {
+ const userId = session?.user?.id
+ const role = session?.user?.role
+
+ fetchClients(role).then((data) => {
+ if (role !== "SuperAdmin" && role !== "Admin-IA") {
+ setClients(data)
+ const filterPerson = data.filter((item) => userId == item.sellerId)
+ setClients(filterPerson)
+ } else {
+ setClients(data)
+ }
+ })
+
+ getUser(userId).then((salesPerson) => {
+ console.log(salesPerson)
+ setcurrentUser(salesPerson)
+ setContracts(salesPerson.contracts)
+
+ if (role != "SuperAdmin" && role == "Sales1") {
+ setsalesId(salesPerson._id)
+ setupSellerId(salesPerson.upSellerId)
+ }
+
+ if (role != "SuperAdmin" && role == "Sales2") {
+ setsalesId(salesPerson._id)
+ setupSellerId(salesPerson.upSellerId)
+ }
+ if (role != "SuperAdmin" && role == "Admin-IA") {
+ setsalesId(salesPerson._id)
+ setupSellerId(salesPerson.upSellerId)
+ }
+ })
+ }
+ }, [session, salesPerson, role])
+
+ //=====================handle has client
+ const handleClient = () => {
+ setHasClient(!hasClient)
+ if (!hasClient) {
+ setClientId("")
+ }
+ }
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setLoading(true)
+
+ function formatDate(dateString) {
+ const options = { year: "numeric", month: "long", day: "numeric" }
+ return new Intl.DateTimeFormat("en-US", options).format(
+ new Date(dateString)
+ )
+ }
+
+ if (dateSigned) {
+ dateSigned = formatDate(dateSigned)
+ }
+ if (clientId) {
+ if (
+ !projectName ||
+ !projectDetails ||
+ !budget ||
+ !salesPerson ||
+ !salesId ||
+ // !upSellerId ||
+ !companyName ||
+ !dateSigned
+ ) {
+ toast({
+ variant: "destructive",
+ title: "All fields required with client.",
+ })
+ setLoading(false)
+ return
+ }
+ } else {
+ if (
+ !projectName ||
+ !projectDetails ||
+ !budget ||
+ !salesPerson ||
+ !salesId ||
+ !upSellerId ||
+ !companyName ||
+ !dateSigned ||
+ !clientName ||
+ !email ||
+ !phone ||
+ !address
+ ) {
+ toast({
+ variant: "destructive",
+ title: "All fields required.",
+ })
+ setLoading(false)
+ return
+ }
+ }
+
+ try {
+ const res = await fetch("api/project", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ projectName,
+ projectDetails,
+ budget,
+ salesPerson,
+ salesId,
+ upSellerId,
+ companyName: companyName ? companyName : null,
+ dateSigned,
+ clientName,
+ email: email ? email : null,
+ phone: phone ? phone : null,
+ address: address ? address : null,
+ clientId: clientId ? clientId : null,
+ callClient: isClientCall,
+ emailClient: isClientEmail,
+ }),
+ })
+
+ if (res.ok) {
+ setLoading(false)
+ await sendUserNotification(projectName, salesPerson, salesPersonEmail?salesPersonEmail:salesPersonsEmail)
+ await sendAdminNotification(projectName, salesPerson, AdminEmail)
+ router.push("/dashboard")
+
+ } else {
+ if (res.status == 409) {
+ toast({
+ variant: "destructive",
+ title: "Client email already exist!",
+ })
+ } else if (res.status == 408) {
+ toast({
+ variant: "destructive",
+ title: "Client name already exist!",
+ })
+ } else {
+ toast({
+ variant: "destructive",
+ title: "project submit failed!",
+ })
+ }
+ setLoading(false)
+ }
+ } catch (error) {
+ console.log("Error during project submit:", error)
+ toast({
+ variant: "destructive",
+ title: `Error during project submit:", ${error}`,
+ })
+ setLoading(false)
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
+ Add New Project
+
+
+
+
+
+ )
+}
diff --git a/app/add-project/page.tsx b/app/add-project/page.tsx
index 78dcf6b..aae52db 100644
--- a/app/add-project/page.tsx
+++ b/app/add-project/page.tsx
@@ -1,640 +1,8 @@
-"use client"
+// app/add-project/page.tsx
+import AddProjectClient from "./AddProjectClient"
-import { useEffect, useState } from "react"
-import * as React from "react"
-import Link from "next/link"
-import { useRouter } from "next/navigation"
-import { format } from "date-fns"
-import {
- ArrowLeftIcon,
- Calendar as CalendarIcon,
- Check,
- ChevronsUpDown,
-} from "lucide-react"
-import { useSession } from "next-auth/react"
+export const dynamic = 'force-dynamic';
-import { fetchCompanies } from "@/lib/company/company"
-import { fetchClients } from "@/lib/fetchClients"
-import { getUser } from "@/lib/getUser"
-import {
- sendAdminNotification,
- sendUserNotification,
-} from "@/lib/notification/sendNotification"
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { Calendar } from "@/components/ui/calendar"
-import { Checkbox } from "@/components/ui/checkbox"
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
-} from "@/components/ui/command"
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover"
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select"
-import { Textarea } from "@/components/ui/textarea"
-import { useToast } from "@/components/ui/use-toast"
-import { Icons } from "@/components/icons"
-
-export default function AddProject() {
- const AdminEmail = process.env.NEXT_PUBLIC_ADMIN_EMAIL
- const [open, setOpen] = useState(false)
- const [openClient, setOpenClient] = useState(false)
- const [value, setValue] = useState("")
- const [valueClient, setValueClient] = useState("")
-
- const { toast } = useToast()
- const router = useRouter()
-
- const { data: session } = useSession()
-
- const [isLoading, setLoading] = useState(false)
- const [projectName, setprojectName] = useState("")
- const [salesPerson, setUserId] = useState("")
- const [salesPersonEmail, setSalesPersonEmail] = useState("")
- const [salesId, setsalesId] = useState()
- const [clientId, setClientId] = useState("")
- const [upSellerId, setupSellerId] = useState()
- const [projectDetails, setprojectDetails] = useState("")
- const [budget, setbudget] = useState("")
- const [companyName, setcompanyName] = useState("image appeal")
- let [dateSigned, setdateSigned] = useState("")
- const [clientName, setclientName] = useState("")
- const [email, setemail] = useState("")
- const [phone, setphone] = useState("")
- const [address, setaddress] = useState("")
- const [Users, setUsers] = useState([])
- const [clients, setClients] = useState([])
- const [Allcompanies, setAllcompanies] = useState()
- const [contracts, setContracts] = useState()
- const [currentUser, setcurrentUser] = useState()
- const [isClientCall, setIsClientCall] = useState(false)
- const [isClientEmail, setIsClientEmail] = useState(false)
- const [hasClient, setHasClient] = useState(false)
- const role = session?.user?.role
- const name = session?.user?.name
- const id = session?.user?.id
- const salesPersonsEmail = session?.user?.email
- console.log(salesPersonsEmail)
-
- useEffect(() => {
- fetch("/api/user")
- .then((response) => response.json())
- .then((data) => {
- const main = data.users
- const users = main.filter((item) => item.role != "SuperAdmin")
- setUsers(users)
- console.log(users)
- })
- .catch((error) => {
- console.error("Error:", error)
- })
-
- if (role != "SuperAdmin") {
- setUserId(name)
-
-
- }
-
- fetchCompanies().then((data) => {
- setAllcompanies(data)
- })
-
- if (session) {
- const userId = session?.user?.id
- const role = session?.user?.role
-
- fetchClients(role).then((data) => {
- if (role !== "SuperAdmin" && role !== "Admin-IA") {
- setClients(data)
- const filterPerson = data.filter((item) => userId == item.sellerId)
- setClients(filterPerson)
- } else {
- setClients(data)
- }
- })
-
- getUser(userId).then((salesPerson) => {
- console.log(salesPerson)
- setcurrentUser(salesPerson)
- setContracts(salesPerson.contracts)
-
- if (role != "SuperAdmin" && role == "Sales1") {
- setsalesId(salesPerson._id)
- setupSellerId(salesPerson.upSellerId)
- }
-
- if (role != "SuperAdmin" && role == "Sales2") {
- setsalesId(salesPerson._id)
- setupSellerId(salesPerson.upSellerId)
- }
- if (role != "SuperAdmin" && role == "Admin-IA") {
- setsalesId(salesPerson._id)
- setupSellerId(salesPerson.upSellerId)
- }
- })
- }
- }, [session, salesPerson, role])
-
- //=====================handle has client
- const handleClient = () => {
- setHasClient(!hasClient)
- if (!hasClient) {
- setClientId("")
- }
- }
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault()
- setLoading(true)
-
- function formatDate(dateString) {
- const options = { year: "numeric", month: "long", day: "numeric" }
- return new Intl.DateTimeFormat("en-US", options).format(
- new Date(dateString)
- )
- }
-
- if (dateSigned) {
- dateSigned = formatDate(dateSigned)
- }
- if (clientId) {
- if (
- !projectName ||
- !projectDetails ||
- !budget ||
- !salesPerson ||
- !salesId ||
- // !upSellerId ||
- !companyName ||
- !dateSigned
- ) {
- toast({
- variant: "destructive",
- title: "All fields required with client.",
- })
- setLoading(false)
- return
- }
- } else {
- if (
- !projectName ||
- !projectDetails ||
- !budget ||
- !salesPerson ||
- !salesId ||
- !upSellerId ||
- !companyName ||
- !dateSigned ||
- !clientName ||
- !email ||
- !phone ||
- !address
- ) {
- toast({
- variant: "destructive",
- title: "All fields required.",
- })
- setLoading(false)
- return
- }
- }
-
- try {
- const res = await fetch("api/project", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- projectName,
- projectDetails,
- budget,
- salesPerson,
- salesId,
- upSellerId,
- companyName: companyName ? companyName : null,
- dateSigned,
- clientName,
- email: email ? email : null,
- phone: phone ? phone : null,
- address: address ? address : null,
- clientId: clientId ? clientId : null,
- callClient: isClientCall,
- emailClient: isClientEmail,
- }),
- })
-
- if (res.ok) {
- setLoading(false)
- await sendUserNotification(projectName, salesPerson, salesPersonEmail?salesPersonEmail:salesPersonsEmail)
- await sendAdminNotification(projectName, salesPerson, AdminEmail)
- router.push("/dashboard")
-
- } else {
- if (res.status == 409) {
- toast({
- variant: "destructive",
- title: "Client email already exist!",
- })
- } else if (res.status == 408) {
- toast({
- variant: "destructive",
- title: "Client name already exist!",
- })
- } else {
- toast({
- variant: "destructive",
- title: "project submit failed!",
- })
- }
- setLoading(false)
- }
- } catch (error) {
- console.log("Error during project submit:", error)
- toast({
- variant: "destructive",
- title: `Error during project submit:", ${error}`,
- })
- setLoading(false)
- }
- }
-
- return (
-
-
-
-
-
-
-
- Add New Project
-
-
-
-
-
- )
-}
+export default function AddProjectPage() {
+ return
+}
\ No newline at end of file
diff --git a/app/api/company/route.js b/app/api/company/route.js
index 51214c4..a405ca7 100644
--- a/app/api/company/route.js
+++ b/app/api/company/route.js
@@ -116,6 +116,7 @@ export async function GET(req) {
if (companyName) {
try {
await connectMongoDB()
+ // SECURITY DEBT: ver SECURITY_DEBT.md #1 — companyName no saneado antes de construir el RegExp (ReDoS + regex injection)
const company = await Company.findOne({
companyName: { $regex: new RegExp("^" + companyName + "$", "i") },
}).select()
diff --git a/app/contracts/page.tsx b/app/contracts/page.tsx
index f6f3faa..7ac23e9 100644
--- a/app/contracts/page.tsx
+++ b/app/contracts/page.tsx
@@ -13,6 +13,8 @@ import SignContract from "@/components/company/SignContract"
import ContactsSkeleton from "./ContactsSkelaton"
+export const dynamic = 'force-dynamic';
+
function replaceSpaceAndLowerCase(inputString) {
const result = inputString.replace(" ", "-").toLowerCase()
return result
diff --git a/app/dashboard/DashboardClient.tsx b/app/dashboard/DashboardClient.tsx
new file mode 100644
index 0000000..7b56d59
--- /dev/null
+++ b/app/dashboard/DashboardClient.tsx
@@ -0,0 +1,89 @@
+"use client"
+import { useEffect } from "react"
+import { useSession } from "next-auth/react"
+import { fetchInvoices } from "@/lib/fetchInvoices"
+import { fetchProjects } from "@/lib/fetchProjects"
+
+import AdminIA from "./AdminIA"
+import SalesOne from "./SalesOne"
+import SalesTwo from "./SalesTwo"
+import SuperAdmin from "./SuperAdmin"
+
+
+export default function DashboardPage() {
+ const { data: session } = useSession()
+ const role = session?.user?.role
+ const username = session?.user?.name
+
+ useEffect(() => {
+ if (session && role == "Sales2") {
+ fetchProjects()
+ .then((apiData) => {
+ if (apiData) {
+ const pendingProjects = apiData.filter(
+ (item) => item.status == "Pending" && item.salesPerson == username
+ )
+ const filteredData = apiData.filter(
+ (item) =>
+ item.status !== "Pending" && item.salesPerson == username
+ )
+
+ const completedproject = apiData.filter(
+ (item) =>
+ item.status == "Complete" && item.salesPerson == username
+ )
+
+ const uniqueClientNames = []
+
+ filteredData.forEach((item) => {
+ if (!uniqueClientNames.includes(item.clientName)) {
+ uniqueClientNames.push(item.clientName)
+ }
+ })
+
+ setcompletedproject(completedproject)
+ setclientName(uniqueClientNames)
+ setpendingProject(pendingProjects)
+ setData(filteredData)
+ setLoading(false)
+ }
+ })
+ .catch((error) => {
+ console.error("Error in component:", error)
+ })
+
+ fetchInvoices()
+ .then((invoiceData) => {
+ if (invoiceData) {
+ const earning = invoiceData.filter(
+ (item) =>
+ item.status == "Paid" &&
+ item.commission_paid == "Yes" &&
+ item.userId == session?.user?.id
+ )
+
+ const total = earning.reduce(
+ (total, item) => total + (item.amount / 100) * item.rate,
+ 0
+ )
+
+ settotalEarn(total)
+ }
+ })
+ .catch((error) => {
+ console.error("Error in component:", error)
+ })
+ }
+ }, [session, role, username])
+
+ return (
+ <>
+
+ {role == "SuperAdmin" &&
}
+ {role == "Admin-IA" &&
}
+ {role == "Sales1" &&
}
+ {role == "Sales2" &&
}
+
+ >
+ )
+}
diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index 0eaa000..5c43473 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -1,88 +1,8 @@
-"use client"
-import { useEffect } from "react"
-import { useSession } from "next-auth/react"
-import { fetchInvoices } from "@/lib/fetchInvoices"
-import { fetchProjects } from "@/lib/fetchProjects"
+// app/dashboard/page.tsx
+import DashboardClient from "./DashboardClient"
-import AdminIA from "./AdminIA"
-import SalesOne from "./SalesOne"
-import SalesTwo from "./SalesTwo"
-import SuperAdmin from "./SuperAdmin"
+export const dynamic = 'force-dynamic';
export default function DashboardPage() {
- const { data: session } = useSession()
- const role = session?.user?.role
- const username = session?.user?.name
-
- useEffect(() => {
- if (session && role == "Sales2") {
- fetchProjects()
- .then((apiData) => {
- if (apiData) {
- const pendingProjects = apiData.filter(
- (item) => item.status == "Pending" && item.salesPerson == username
- )
- const filteredData = apiData.filter(
- (item) =>
- item.status !== "Pending" && item.salesPerson == username
- )
-
- const completedproject = apiData.filter(
- (item) =>
- item.status == "Complete" && item.salesPerson == username
- )
-
- const uniqueClientNames = []
-
- filteredData.forEach((item) => {
- if (!uniqueClientNames.includes(item.clientName)) {
- uniqueClientNames.push(item.clientName)
- }
- })
-
- setcompletedproject(completedproject)
- setclientName(uniqueClientNames)
- setpendingProject(pendingProjects)
- setData(filteredData)
- setLoading(false)
- }
- })
- .catch((error) => {
- console.error("Error in component:", error)
- })
-
- fetchInvoices()
- .then((invoiceData) => {
- if (invoiceData) {
- const earning = invoiceData.filter(
- (item) =>
- item.status == "Paid" &&
- item.commission_paid == "Yes" &&
- item.userId == session?.user?.id
- )
-
- const total = earning.reduce(
- (total, item) => total + (item.amount / 100) * item.rate,
- 0
- )
-
- settotalEarn(total)
- }
- })
- .catch((error) => {
- console.error("Error in component:", error)
- })
- }
- }, [session, role, username])
-
- return (
- <>
-
- {role == "SuperAdmin" &&
}
- {role == "Admin-IA" &&
}
- {role == "Sales1" &&
}
- {role == "Sales2" &&
}
-
- >
- )
-}
+ return
+}
\ No newline at end of file
diff --git a/app/settings/manage-contracts/AddContract.tsx b/app/settings/manage-contracts/AddContract.tsx
index 8f302ab..f62ea9c 100644
--- a/app/settings/manage-contracts/AddContract.tsx
+++ b/app/settings/manage-contracts/AddContract.tsx
@@ -141,6 +141,7 @@ export default function AddContract(props) {
offerCompany.map(
(singleCompany, index) =>
!activeCompany.includes(singleCompany.companyName) && (
+ // eslint-disable-next-line react/jsx-key -- deuda conocida, ver SECURITY_DEBT.md #2, pendiente de fix
!activeCompany.includes(singleCompany.companyName) && (
+ // eslint-disable-next-line react/jsx-key -- deuda conocida, ver SECURITY_DEBT.md #2, pendiente de fix
{
{singleUser.contracts.map((contract, index) => (
+ // eslint-disable-next-line react/jsx-key -- deuda conocida, ver SECURITY_DEBT.md #2, pendiente de fix
{
variant: "destructive",
title: "All fields are required",
})
+ // eslint-disable-next-line security/detect-possible-timing-attacks -- comparación de dos campos del mismo formulario, no de un secreto contra un valor almacenado
} else if (password !== confirmPassword) {
toast({
variant: "destructive",
diff --git a/components/SetPassword.tsx b/components/SetPassword.tsx
index 18713ef..32a355b 100644
--- a/components/SetPassword.tsx
+++ b/components/SetPassword.tsx
@@ -31,6 +31,7 @@ export default function SetPassword(props) {
return
}
+ // eslint-disable-next-line security/detect-possible-timing-attacks -- comparación de dos campos del mismo formulario, no de un secreto contra un valor almacenado
if (password != verifyPassword) {
console.log("The password you entered does not match")
toast({
diff --git a/components/TabComponent.tsx b/components/TabComponent.tsx
index 64c45c2..abcb8ca 100644
--- a/components/TabComponent.tsx
+++ b/components/TabComponent.tsx
@@ -20,6 +20,7 @@ const TabComponent = ({ tabs }) => {
))}
+ {/* eslint-disable-next-line security/detect-object-injection -- activeTab solo puede ser un Ãndice generado por el propio .map() sobre tabs, nunca input externo */}
{tabs[activeTab].content}