From daca53ce8cc92beec29bcfcb41bc046c05872e6e Mon Sep 17 00:00:00 2001 From: Brey Rivera Date: Sun, 2 Mar 2025 13:12:26 -0500 Subject: [PATCH 01/33] Update Docker workflow to support new branch naming and improve image tagging (#51) --- .github/workflows/docker.yml | 36 ++++++++++++++++++++++++++---------- README.md | 18 ++++++++++-------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8a0590c..f89c345 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -3,35 +3,51 @@ on: pull_request: branches: - main + - dev types: - closed + push: + branches: + - 'release/*' + jobs: publish_image: - if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'r') + if: github.event.pull_request.merged == true runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v4 - - name: Extract branch name - id: extract_branch - run: echo "BRANCH_NAME=$(echo ${GITHUB_HEAD_REF})" >> $GITHUB_ENV + - name: Extract pull request ID + id: extract_pr_id + run: echo "PR_ID=$(echo ${GITHUB_EVENT_PULL_REQUEST_NUMBER})" >> $GITHUB_ENV + + - name: Determine image tag + id: determine_tag + run: | + if [[ "${GITHUB_REF}" == refs/heads/release/* ]]; then + echo "IMAGE_TAG=$(echo ${GITHUB_REF} | sed 's/refs\/heads\/release\///')" >> $GITHUB_ENV + elif [[ "${GITHUB_REF}" == refs/heads/dev ]]; then + echo "IMAGE_TAG=dev-pr-${{ env.PR_ID }}" >> $GITHUB_ENV + else + echo "IMAGE_TAG=latest" >> $GITHUB_ENV + fi - name: build backend image run: | - docker build -f backend/Dockerfile -t breyr/top-backend:${{ env.BRANCH_NAME }} . + docker build -f backend/Dockerfile -t breyr/top-backend:${{ env.IMAGE_TAG }} . - name: build frontend image run: | - docker build -f frontend/Dockerfile -t breyr/top-frontend:${{ env.BRANCH_NAME }} . + docker build -f frontend/Dockerfile -t breyr/top-frontend:${{ env.IMAGE_TAG }} . - name: build interconnect-api image run: | - docker build -f interconnect-api/Dockerfile -t breyr/top-interconnectapi:${{ env.BRANCH_NAME }} . + docker build -f interconnect-api/Dockerfile -t breyr/top-interconnectapi:${{ env.IMAGE_TAG }} . - name: push images run: | docker login -u breyr -p ${{ secrets.DOCKER_HUB_TOKEN }} - docker push breyr/top-backend:${{ env.BRANCH_NAME }} - docker push breyr/top-frontend:${{ env.BRANCH_NAME }} - docker push breyr/top-interconnectapi:${{ env.BRANCH_NAME }} \ No newline at end of file + docker push breyr/top-backend:${{ env.IMAGE_TAG }} + docker push breyr/top-frontend:${{ env.IMAGE_TAG }} + docker push breyr/top-interconnectapi:${{ env.IMAGE_TAG }} \ No newline at end of file diff --git a/README.md b/README.md index 9fec691..8e6a00b 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,7 @@ Use JIRA to create a new branch from `dev` DATABASE_URL="postgres://demo:demo@localhost:5432/demo" SECRET_KEY="my_secret_key" ``` -4. Run `npm install` in the following locations: - 1. `/common` - 2. `/backend` - 3. `/frontend` +3. Run `npm install` in the root directory of this project. ### Starting Development Environment @@ -34,13 +31,18 @@ When you are ready to submit a pull request, make sure you merge from your branc ## Releases -Peridocially we will create release branches from `dev`. These branches will follow these naming conventions: +Peridocially we will create release branches from `dev`. These branches will follow these naming convention: -`r-devtest` or `r---` +`release---` -**`devtest`** is used for rapid iteration for testing docker images, or you could test locally. +**`--`** is used to create a new image per release, tagged with `release---` -**`--`** is used to create a new image per release, tagged with `r---` +### Steps for release: + +1. Create release branch from dev +2. Make any changes in that release branch to make sure everything builds +3. Merge release branch into main +4. Merge main into dev ## Images From 0a66c419e130fa5d78f4ffc7464a5c302db0c192 Mon Sep 17 00:00:00 2001 From: Brey Rivera Date: Sun, 2 Mar 2025 13:19:42 -0500 Subject: [PATCH 02/33] Refactor Docker workflow to use correct pull request number syntax for image tagging (#52) --- .github/workflows/docker.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f89c345..30443cb 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -12,7 +12,6 @@ on: jobs: publish_image: - if: github.event.pull_request.merged == true runs-on: ubuntu-latest steps: - name: checkout @@ -20,7 +19,7 @@ jobs: - name: Extract pull request ID id: extract_pr_id - run: echo "PR_ID=$(echo ${GITHUB_EVENT_PULL_REQUEST_NUMBER})" >> $GITHUB_ENV + run: echo "PR_ID=${{ github.event.pull_request.number }}" >> $GITHUB_ENV - name: Determine image tag id: determine_tag From 39263130bd942141999304333bac4bd472a229e4 Mon Sep 17 00:00:00 2001 From: Brey Rivera Date: Sun, 2 Mar 2025 15:35:21 -0500 Subject: [PATCH 03/33] Update action (#54) * Refactor Docker workflow to use correct pull request number syntax for image tagging * Refactor imports to use simplified paths and update .gitignore to include tsconfig build info --- README.md | 2 +- backend/Dockerfile | 1 + backend/src/controllers/DeviceController.ts | 2 +- backend/src/controllers/TopologyController.ts | 2 +- backend/src/controllers/UserController.ts | 2 +- .../repositories/PrismaDeviceRepository.ts | 1 - .../repositories/PrismaTopologyRepository.ts | 18 +- .../src/repositories/PrismaUserRepository.ts | 2 +- backend/src/server.ts | 8 +- backend/src/services/TopologyService.ts | 2 +- backend/src/services/UserService.ts | 2 +- backend/src/types/classInterfaces.ts | 2 +- backend/tsconfig.json | 23 ++- common/.gitignore | 3 +- common/dist/index.d.ts | 160 ++++++++++++++++++ common/dist/index.js | 9 + common/package.json | 5 +- frontend/Dockerfile | 12 +- frontend/src/components/CreateTopology.tsx | 2 +- frontend/src/components/TopologyCard.tsx | 8 +- .../components/reactflow/TopologyCanvas.tsx | 2 +- .../reactflow/overlayui/NodePicker.tsx | 2 +- frontend/src/components/table/UsersTable.tsx | 24 +-- frontend/src/context/AuthContext.tsx | 2 +- frontend/src/hooks/useSocket.ts | 2 +- frontend/src/lib/authenticatedApi.ts | 2 +- frontend/src/models/Device.ts | 2 +- frontend/src/models/User.ts | 2 +- frontend/src/pages/AllUserTopologiesPage.tsx | 2 +- frontend/src/pages/UserArchivedTopologies.tsx | 2 +- frontend/src/pages/UserTopologies.tsx | 2 +- frontend/tsconfig.app.json | 7 +- frontend/vite.config.ts | 13 +- package.json | 7 +- 34 files changed, 272 insertions(+), 65 deletions(-) create mode 100644 common/dist/index.d.ts create mode 100644 common/dist/index.js diff --git a/README.md b/README.md index 8e6a00b..a2a6b0a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Use JIRA to create a new branch from `dev` This runs the postgres db and interconnect api container. -2. Run `npm run dev` within `/backend` first and then `/frontend`, order matters. +2. Run `npm run dev:backend` and `npm run dev:frontend` within the project root. ### Pull Requests diff --git a/backend/Dockerfile b/backend/Dockerfile index 6485ede..03ba8b6 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -23,6 +23,7 @@ COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/backend/node_modules ./node_modules COPY --from=builder /app/backend/dist ./dist COPY --from=builder /app/backend/prisma ./prisma +COPY --from=builder /app/common ./common EXPOSE 3000 CMD ["node", "dist/server.js"] \ No newline at end of file diff --git a/backend/src/controllers/DeviceController.ts b/backend/src/controllers/DeviceController.ts index 48c554b..ca51445 100644 --- a/backend/src/controllers/DeviceController.ts +++ b/backend/src/controllers/DeviceController.ts @@ -1,6 +1,6 @@ import type { Device, DeviceType, IconType } from "@prisma/client"; +import { EmitTypes } from "common"; import type { NextFunction, Response } from "express"; -import { EmitTypes } from "../../../common/src/index"; import { DIContainer } from "../config/DIContainer"; import { io } from "../server"; import type { AuthenticatedRequest } from '../types/types'; diff --git a/backend/src/controllers/TopologyController.ts b/backend/src/controllers/TopologyController.ts index c1b0d02..b74b9f8 100644 --- a/backend/src/controllers/TopologyController.ts +++ b/backend/src/controllers/TopologyController.ts @@ -1,5 +1,5 @@ +import type { CreateTopologyRequestPayload } from "common"; import { NextFunction, Response } from "express"; -import type { CreateTopologyRequestPayload } from "../../../common/src/index"; import { DIContainer } from "../config/DIContainer"; import { AuthenticatedRequest, UpdateTopologyDTO } from "../types/types"; import { checkForAccountType, checkForTopologyId, checkForUserId } from "../utils/validation"; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index 9cee85a..a949c97 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -1,7 +1,7 @@ import type { AppUser } from '@prisma/client'; import bcrypt from 'bcryptjs'; +import type { LoginRequestPayload, LoginResponsePayload, RegisterUserRequestPayload } from 'common'; import { NextFunction, Request, Response } from "express"; -import type { LoginRequestPayload, LoginResponsePayload, RegisterUserRequestPayload } from '../../../common/src/index'; import { DIContainer } from "../config/DIContainer"; import { AuthenticatedRequest, CustomJwtPayload } from '../types/types'; import { createJwtToken, createRefreshToken } from '../utils/jwt'; diff --git a/backend/src/repositories/PrismaDeviceRepository.ts b/backend/src/repositories/PrismaDeviceRepository.ts index 539685e..a119bac 100644 --- a/backend/src/repositories/PrismaDeviceRepository.ts +++ b/backend/src/repositories/PrismaDeviceRepository.ts @@ -1,6 +1,5 @@ import { DeviceType, IconType, PrismaClient, type Device } from "@prisma/client"; import bcrypt from 'bcryptjs'; -import { EmitTypes } from "../../../common/src/index"; import { IDeviceRepository } from "../types/classInterfaces"; export class PrismaDeviceRepository implements IDeviceRepository { diff --git a/backend/src/repositories/PrismaTopologyRepository.ts b/backend/src/repositories/PrismaTopologyRepository.ts index b12cf0a..365b02d 100644 --- a/backend/src/repositories/PrismaTopologyRepository.ts +++ b/backend/src/repositories/PrismaTopologyRepository.ts @@ -1,5 +1,5 @@ import { Prisma, PrismaClient, type Topology } from "@prisma/client"; -import type { CreateTopologyRequestPayload, ReactFlowState } from "../../../common/src/index"; +import type { CreateTopologyRequestPayload, ReactFlowState } from "common"; import { ITopologyRepository } from "../types/classInterfaces"; import { UpdateTopologyDTO } from "../types/types"; @@ -44,7 +44,7 @@ export class PrismaTopologyRepository implements ITopologyRepository { })); }); } - + findAll(userId: number): Promise { return this.prismaClient.topology.findMany({ where: { userId } @@ -77,15 +77,15 @@ export class PrismaTopologyRepository implements ITopologyRepository { findUnique(topologyId: number): Promise { return this.prismaClient.topology.findUnique({ - where: { id: topologyId } + where: { id: topologyId } }).then((topology) => { - if (!topology) return null; - return { - ...topology, - reactFlowState: PrismaTopologyRepository.convertReactFlowState(topology.reactFlowState) - }; + if (!topology) return null; + return { + ...topology, + reactFlowState: PrismaTopologyRepository.convertReactFlowState(topology.reactFlowState) + }; }); - } + } update(topologyId: number, topologyData: UpdateTopologyDTO): Promise { return this.prismaClient.topology.update({ diff --git a/backend/src/repositories/PrismaUserRepository.ts b/backend/src/repositories/PrismaUserRepository.ts index a6b9354..7fb80d8 100644 --- a/backend/src/repositories/PrismaUserRepository.ts +++ b/backend/src/repositories/PrismaUserRepository.ts @@ -1,6 +1,6 @@ import { AccountStatus, PrismaClient, type AppUser } from "@prisma/client"; import bcrypt from 'bcryptjs'; -import type { RegisterUserRequestPayload } from "../../../common/src/index"; +import type { RegisterUserRequestPayload } from "common"; import { IUserRepository } from "../types/classInterfaces"; export class PrismaUserRepository implements IUserRepository { diff --git a/backend/src/server.ts b/backend/src/server.ts index e2167e0..b07649c 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,8 +1,8 @@ -import { PrismaClient } from "@prisma/client"; +import { Device, PrismaClient } from "@prisma/client"; import { execSync } from "child_process"; +import { EmitTypes } from 'common'; import { createServer } from "http"; import { Server, Socket } from "socket.io"; -import { EmitTypes } from '../../common/src/index'; import app from './app'; const PORT = process.env.PORT || 3000; @@ -45,12 +45,12 @@ async function start() { io.on('connection', (socket: Socket) => { console.log('Client connected'); - socket.on(EmitTypes.BookDevice, (data) => { + socket.on(EmitTypes.BookDevice, (data: { bookedDevice: Device }) => { // send data to all currently connected clients io.emit(EmitTypes.BookDevice, data); }); - socket.on(EmitTypes.UnbookDevice, (data) => { + socket.on(EmitTypes.UnbookDevice, (data: { unbookedDevice: Device }) => { // send data to all currently connected clients io.emit(EmitTypes.UnbookDevice, data); }); diff --git a/backend/src/services/TopologyService.ts b/backend/src/services/TopologyService.ts index 37f7363..30960e0 100644 --- a/backend/src/services/TopologyService.ts +++ b/backend/src/services/TopologyService.ts @@ -1,5 +1,5 @@ import type { Topology } from "@prisma/client"; -import type { CreateTopologyRequestPayload } from "../../../common/src/index"; +import type { CreateTopologyRequestPayload } from "common"; import type { ITopologyRepository, ITopologyService } from "../types/classInterfaces"; import { UpdateTopologyDTO } from "../types/types"; diff --git a/backend/src/services/UserService.ts b/backend/src/services/UserService.ts index 2d7a7b9..9c9c513 100644 --- a/backend/src/services/UserService.ts +++ b/backend/src/services/UserService.ts @@ -1,5 +1,5 @@ import type { AppUser } from "@prisma/client"; -import type { RegisterUserRequestPayload } from "../../../common/src/index"; +import type { RegisterUserRequestPayload } from "common"; import { IUserRepository, IUserService } from "../types/classInterfaces"; import { validateEmail, ValidationError } from "../utils/validation"; diff --git a/backend/src/types/classInterfaces.ts b/backend/src/types/classInterfaces.ts index bd78329..3ef05ef 100644 --- a/backend/src/types/classInterfaces.ts +++ b/backend/src/types/classInterfaces.ts @@ -1,6 +1,6 @@ // holds interfaces similar to C# import { AppConfig, AppUser, Connection, Device, DeviceType, IconType, Topology } from "@prisma/client"; -import type { CreateConnectionRequestPayload, CreateTopologyRequestPayload, RegisterUserRequestPayload } from "../../../common/src/index"; +import type { CreateConnectionRequestPayload, CreateTopologyRequestPayload, RegisterUserRequestPayload } from "common"; import { UpdateTopologyDTO } from "./types"; export interface IUserRepository { diff --git a/backend/tsconfig.json b/backend/tsconfig.json index fc75000..ff240c9 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -1,20 +1,27 @@ { "compilerOptions": { - "target": "ES6", - "module": "commonjs", + "baseUrl": ".", + "paths": { + "common/*": [ + "../common/src/*" + ] + }, "outDir": "./dist", - "rootDir": "./src", + "module": "commonjs", + "target": "es6", + "lib": [ + "es6" + ], "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, - "references": [ - { - "path": "../common" - } - ], "include": [ "src/**/*" + ], + "exclude": [ + "node_modules", + "dist" ] } \ No newline at end of file diff --git a/common/.gitignore b/common/.gitignore index b512c09..fbb82ad 100644 --- a/common/.gitignore +++ b/common/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +node_modules +tsconfig.tsbuildinfo \ No newline at end of file diff --git a/common/dist/index.d.ts b/common/dist/index.d.ts new file mode 100644 index 0000000..6b8acc9 --- /dev/null +++ b/common/dist/index.d.ts @@ -0,0 +1,160 @@ +export type AccountType = 'USER' | 'ADMIN' | 'OWNER'; +export type AccountStatus = 'NOTCREATED' | 'PENDING' | 'ACCEPTED'; +export type DeviceType = 'LAB' | 'INTERCONNECT'; +export type IconType = 'ROUTER' | 'SWITCH' | 'EXTERNAL' | 'SERVER'; +type Node = { + id: string; + type: string; + position: { + x: number; + y: number; + }; + data: { + label: string; + }; + measured?: { + width: number; + height: number; + }; + selected?: boolean; + dragging?: boolean; +}; +type Edge = { + source: string; + target: string; + id: string; +}; +type Viewport = { + x: number; + y: number; + zoom: number; +}; +export type ReactFlowState = { + nodes: Array; + edges: Array; + viewport: Viewport; +}; +export interface LoginRequestPayload { + usernameOrEmail: string; + password: string; +} +export interface LoginResponsePayload { + message: string; + data: { + token: string; + }; +} +export interface RegisterUserRequestPayload { + username: string; + email: string; + password: string; + accountType: AccountType; +} +export interface RegisterUserResponsePayload { + message: string; + data: { + user?: PartialAppUser; + }; +} +export interface PartialAppUser { + id: number; + username: string; + email: string; + password: string; + tempPassword: string; + accountType: AccountType; + accountStatus: AccountStatus; +} +export interface UserJwtPayload { + id: number; + username: string; + email: string; + accountType: AccountType; + exp: number; +} +export interface Topology { + id: number; + userId: number; + name: string; + thumbnail: { + [key: number]: number; + } | string; + reactFlowState: ReactFlowState | null; + expiresOn: Date; + archived: boolean; + createdAt: Date; + updatedAt: Date; +} +export interface CreateTopologyRequestPayload { + name: string; +} +export interface CreateDeviceRequestPayload { + deviceNumber: 1 | 2; + userId: number | null; + topologyId: number | null; + name: string; + model: string; + serialNumber: string; + ipAddress: string | null; + description: string | null; + password: string | null; + username: string | null; + secretPassword: string | null; + ports: string; + type: DeviceType; + icon: IconType | null; +} +export interface UpdateDeviceRequestPayload { + name?: string; + model?: string; + serialNumber?: string; + ipAddress?: string | null; + description?: string | null; + password?: string | null; + username?: string | null; + secretPassword?: string | null; + ports?: string; + type?: DeviceType; + icon?: IconType; +} +export interface Connection { + id?: number; + labDeviceName: string; + labDevicePort: string; + interconnectDeviceName: string; + interconnectDevicePort: string; +} +export interface CreateConnectionRequestPayload { + labDeviceName: string; + labDevicePort: string; + interconnectDeviceName: string; + interconnectDevicePort: string; +} +export interface UpdateConnectionRequestPayload { + labDeviceName?: string; + labDevicePort?: string; + interconnectDeviceName?: string; + interconnectDevicePort?: string; +} +export interface LinkRequest { + interconnect1IP: string; + interconnect1Prefix: string; + interconnect2IP: string; + interconnect2Prefix: string; + interconnectPortID1: number; + interconnectPortID2: number; + username: string; + password: string; + secret: string; +} +export interface LinkResponse { + status: string; + message: string; + device1_output: string; + device2_output: string; +} +export declare enum EmitTypes { + BookDevice = "BookDevice", + UnbookDevice = "UnbookDevice" +} +export {}; diff --git a/common/dist/index.js b/common/dist/index.js new file mode 100644 index 0000000..c847157 --- /dev/null +++ b/common/dist/index.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EmitTypes = void 0; +// Socket.IO Enums +var EmitTypes; +(function (EmitTypes) { + EmitTypes["BookDevice"] = "BookDevice"; + EmitTypes["UnbookDevice"] = "UnbookDevice"; +})(EmitTypes || (exports.EmitTypes = EmitTypes = {})); diff --git a/common/package.json b/common/package.json index 371d275..70e2f0a 100644 --- a/common/package.json +++ b/common/package.json @@ -4,7 +4,8 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { - "build": "tsc" + "build": "tsc", + "watch": "tsc -w" }, "author": "", "license": "ISC", @@ -13,4 +14,4 @@ "@types/node": "^22.10.5", "typescript": "^5.7.3" } -} +} \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile index d200850..2c0a1ab 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,12 +1,22 @@ # build stage FROM node:22.9 AS build WORKDIR /app + +# copy root package.json and lock file COPY package*.json ./ + +# copy workspace package.json files COPY common/package*.json common/ COPY frontend/package*.json frontend/ -RUN rm -rf node_modules package-lock.json && npm install + +# install dependencies for all workspaces +RUN npm install + +# copy the rest of the files COPY common ./common COPY frontend ./frontend + +# build the frontend RUN npm run build:frontend # production stage diff --git a/frontend/src/components/CreateTopology.tsx b/frontend/src/components/CreateTopology.tsx index ff0a4f2..81f320a 100644 --- a/frontend/src/components/CreateTopology.tsx +++ b/frontend/src/components/CreateTopology.tsx @@ -1,6 +1,6 @@ +import type { CreateTopologyRequestPayload } from 'common'; import { PlusCircle } from 'lucide-react'; import { useNavigate } from "react-router-dom"; -import type { CreateTopologyRequestPayload } from '../../../common/src/index'; import { useAuth } from '../hooks/useAuth'; const CreateTopology = () => { diff --git a/frontend/src/components/TopologyCard.tsx b/frontend/src/components/TopologyCard.tsx index 733de3a..d44069d 100644 --- a/frontend/src/components/TopologyCard.tsx +++ b/frontend/src/components/TopologyCard.tsx @@ -1,10 +1,10 @@ +import { Topology } from 'common'; import { Image, Trash } from "lucide-react"; import React, { useEffect, useState } from 'react'; import { useNavigate } from "react-router-dom"; -import { Topology } from '../../../common/src/index'; +import { useAuth } from "../hooks/useAuth.ts"; import useContextMenu from "../hooks/useContextMenu.ts"; import DeletionModal from "./DeletionModal.tsx"; -import { useAuth } from "../hooks/useAuth.ts"; interface TopologyProps extends Topology { onDelete: (topologyId: number) => void; @@ -95,7 +95,7 @@ const TopologyCard: React.FC = ({
{thumbnailSrc ? ( @@ -120,7 +120,7 @@ const TopologyCard: React.FC = ({

row.accountType, - cell: (row, index) => + cell: (row, index) => row.accountType === 'OWNER' ? ( ) : ( - - ), + + ), }, { name: 'Account Status', diff --git a/frontend/src/context/AuthContext.tsx b/frontend/src/context/AuthContext.tsx index a11488b..6c77fbd 100644 --- a/frontend/src/context/AuthContext.tsx +++ b/frontend/src/context/AuthContext.tsx @@ -1,6 +1,6 @@ +import type { AccountStatus, AccountType, LoginResponsePayload, RegisterUserResponsePayload } from 'common'; import { jwtDecode, JwtPayload } from 'jwt-decode'; import { createContext, ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; -import type { AccountStatus, AccountType, LoginResponsePayload, RegisterUserResponsePayload } from '../../../common/src/index'; import { ApiClient } from "../lib/authenticatedApi.ts"; export interface CustomJwtPayload extends JwtPayload { diff --git a/frontend/src/hooks/useSocket.ts b/frontend/src/hooks/useSocket.ts index 561209a..12f4519 100644 --- a/frontend/src/hooks/useSocket.ts +++ b/frontend/src/hooks/useSocket.ts @@ -1,5 +1,5 @@ +import { EmitTypes } from 'common'; import { useCallback, useEffect, useState } from "react"; -import { EmitTypes } from '../../../common/src/index'; import SocketService from "../services/SocketService"; export const useSocket = () => { diff --git a/frontend/src/lib/authenticatedApi.ts b/frontend/src/lib/authenticatedApi.ts index f89ccff..b85317d 100644 --- a/frontend/src/lib/authenticatedApi.ts +++ b/frontend/src/lib/authenticatedApi.ts @@ -1,4 +1,4 @@ -import type { CreateConnectionRequestPayload, DeviceType, IconType, LinkRequest, LinkResponse, PartialAppUser, Topology } from "../../../common/src/index"; +import type { CreateConnectionRequestPayload, DeviceType, IconType, LinkRequest, LinkResponse, PartialAppUser, Topology } from "common"; import { Connection } from "../models/Connection"; import { Device } from "../models/Device"; diff --git a/frontend/src/models/Device.ts b/frontend/src/models/Device.ts index fd58362..2f72dc0 100644 --- a/frontend/src/models/Device.ts +++ b/frontend/src/models/Device.ts @@ -1,4 +1,4 @@ -import { DeviceType, IconType } from "../../../common/src/index"; +import { DeviceType, IconType } from "common"; export class Device { id: number; diff --git a/frontend/src/models/User.ts b/frontend/src/models/User.ts index e30fd83..bdb6cab 100644 --- a/frontend/src/models/User.ts +++ b/frontend/src/models/User.ts @@ -1,4 +1,4 @@ -import { AccountStatus, AccountType, PartialAppUser } from "../../../common/src/index"; +import { AccountStatus, AccountType, PartialAppUser } from "common"; export class User { id: number; diff --git a/frontend/src/pages/AllUserTopologiesPage.tsx b/frontend/src/pages/AllUserTopologiesPage.tsx index 5ee51a7..ed1c721 100644 --- a/frontend/src/pages/AllUserTopologiesPage.tsx +++ b/frontend/src/pages/AllUserTopologiesPage.tsx @@ -1,5 +1,5 @@ +import type { PartialAppUser, Topology } from 'common'; import { useEffect, useState } from 'react'; -import type { PartialAppUser, Topology } from '../../../common/src/index'; import TopologyCard from '../components/TopologyCard'; import { useAuth } from '../hooks/useAuth'; import { User } from '../models/User'; diff --git a/frontend/src/pages/UserArchivedTopologies.tsx b/frontend/src/pages/UserArchivedTopologies.tsx index fdac230..43514ca 100644 --- a/frontend/src/pages/UserArchivedTopologies.tsx +++ b/frontend/src/pages/UserArchivedTopologies.tsx @@ -1,6 +1,6 @@ +import { Topology } from "common"; import { Loader2 } from "lucide-react"; import { useEffect, useState } from "react"; -import { Topology } from "../../../common/src/index"; import TopologyCard from "../components/TopologyCard"; import { useAuth } from "../hooks/useAuth"; diff --git a/frontend/src/pages/UserTopologies.tsx b/frontend/src/pages/UserTopologies.tsx index 9d04929..2fdb0e4 100644 --- a/frontend/src/pages/UserTopologies.tsx +++ b/frontend/src/pages/UserTopologies.tsx @@ -1,7 +1,7 @@ import { Node } from "@xyflow/react"; +import type { Topology } from "common"; import { Loader2 } from "lucide-react"; import { useEffect, useState } from "react"; -import type { Topology } from "../../../common/src/index"; import CreateTopology from "../components/CreateTopology"; import TopologyCard from "../components/TopologyCard"; import { useAuth } from "../hooks/useAuth"; diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index 05da95a..c4f93c3 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -24,7 +24,12 @@ "noImplicitAny": false, "types": [ "vite-plugin-svgr/client" - ] + ], + "paths": { + "common/*": [ + "../common/src/*" + ] + } }, "references": [ { diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index f9ae569..7603500 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,4 +1,5 @@ import react from '@vitejs/plugin-react'; +import path from 'path'; import { defineConfig } from 'vite'; import svgr from 'vite-plugin-svgr'; @@ -8,6 +9,11 @@ export default defineConfig(({ command }) => { if (command === 'build') { return { plugins, + resolve: { + alias: { + 'common': path.resolve(__dirname, '../common/src') + } + } } } else { return { @@ -76,7 +82,12 @@ export default defineConfig(({ command }) => { } } }, - plugins + plugins, + resolve: { + alias: { + 'common': path.resolve(__dirname, '../common/src') + } + } } } }) \ No newline at end of file diff --git a/package.json b/package.json index e1f2930..342dde5 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,12 @@ "build": "npm run build --workspaces", "build:common": "npm run build --workspace common", "build:backend": "npm run build:common && npm run build --workspace backend", - "build:frontend": "npm run build:common && npm run build --workspace frontend" + "build:frontend": "npm run build:common && npm run build --workspace frontend", + "dev:common": "npm run watch --workspace common", + "dev:backend": "npm run dev:common & npm run dev --workspace backend", + "dev:frontend": "npm run dev:common & npm run dev --workspace frontend" }, "dependencies": { "express": "^4.21.2" } -} +} \ No newline at end of file From d7f5fb4b90d0be58d42cfd598092ea6c93fc373c Mon Sep 17 00:00:00 2001 From: Tyler Mui <84096411+Tylermui@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:48:59 -0500 Subject: [PATCH 04/33] Red 76 UI changes (#55) --- frontend/src/components/CreateTopology.tsx | 6 +-- frontend/src/components/DashboardNav.tsx | 6 +-- frontend/src/components/DeletionModal.tsx | 44 ++++++++++++++----- frontend/src/components/DeviceModal.tsx | 10 ++--- .../src/components/InterconnectDeviceCard.tsx | 24 +++++++--- frontend/src/components/SettingsModal.tsx | 15 +++---- frontend/src/components/TopologyCard.tsx | 5 +-- frontend/src/components/UserSetupModal.tsx | 10 ++--- .../components/reactflow/TopologyCanvas.tsx | 5 +-- .../components/reactflow/edges/CustomEdge.tsx | 6 +-- .../reactflow/overlayui/ContextMenu.tsx | 26 ++++++++--- .../reactflow/overlayui/CreateLinkModal.tsx | 20 +++++++-- .../reactflow/overlayui/DeleteLinkModal.tsx | 20 +++++++-- .../reactflow/overlayui/DeviceAccordion.tsx | 10 ++--- .../overlayui/DragAndDropContainer.tsx | 18 +++++--- .../reactflow/overlayui/DraggableItem.tsx | 13 ++---- .../src/components/table/ConnectionsTable.tsx | 2 +- .../src/components/table/LabDevicesTable.tsx | 2 +- frontend/src/lib/helpers.ts | 10 ++++- frontend/src/pages/AllUserTopologiesPage.tsx | 8 ++-- frontend/src/pages/FinishOnboard.tsx | 4 +- frontend/src/pages/Index.tsx | 2 +- frontend/src/pages/UserArchivedTopologies.tsx | 2 +- frontend/src/pages/UserCreate.tsx | 2 +- frontend/src/routes/dashboard.tsx | 10 ++--- frontend/src/routes/topology.tsx | 6 +-- frontend/src/static/global.css | 19 +++++--- 27 files changed, 191 insertions(+), 114 deletions(-) diff --git a/frontend/src/components/CreateTopology.tsx b/frontend/src/components/CreateTopology.tsx index 81f320a..2d672a7 100644 --- a/frontend/src/components/CreateTopology.tsx +++ b/frontend/src/components/CreateTopology.tsx @@ -22,9 +22,9 @@ const CreateTopology = () => { return (
- -

Create Topology

+ className="my-5 rounded-md size-56 border-dashed border-2 flex flex-col justify-center items-center gap-3 text-gray-300 transition-all duration-300 ease-in-out transform hover:scale-95 hover:cursor-pointer hover:border-gray-200"> + +

Create Topology

); }; diff --git a/frontend/src/components/DashboardNav.tsx b/frontend/src/components/DashboardNav.tsx index 6040258..47888ee 100644 --- a/frontend/src/components/DashboardNav.tsx +++ b/frontend/src/components/DashboardNav.tsx @@ -1,4 +1,4 @@ -import { ChartNetwork } from "lucide-react"; +import { GraduationCap } from "lucide-react"; import { useNavigate } from "react-router-dom"; import { useAuth } from "../hooks/useAuth"; import { useState } from "react"; @@ -20,8 +20,8 @@ export default function DashboardNav() { return (