From 288bc17ffebc550541d8f09d7bff8ec82888c214 Mon Sep 17 00:00:00 2001 From: Andrew Trieu Date: Sat, 23 Nov 2024 18:29:38 +0200 Subject: [PATCH 1/6] feat: Add GitHub Actions workflow for Amplify deployment and configuration --- .github/workflows/amplify-deployment.yml | 79 ++++++++++++++++++++++++ tasker-client/amplify.yml | 25 ++++++++ 2 files changed, 104 insertions(+) create mode 100644 .github/workflows/amplify-deployment.yml create mode 100644 tasker-client/amplify.yml diff --git a/.github/workflows/amplify-deployment.yml b/.github/workflows/amplify-deployment.yml new file mode 100644 index 0000000..88c58ac --- /dev/null +++ b/.github/workflows/amplify-deployment.yml @@ -0,0 +1,79 @@ +name: Set Amplify Environment Variables and Trigger Deployment + +permissions: + id-token: write + contents: read + +on: + push: + branches: + - main + paths: + - "tasker-client/**" + +jobs: + checkout-code: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + install-cli: + runs-on: ubuntu-latest + needs: checkout-code + steps: + - name: Install AWS CLI + run: | + curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" + unzip awscliv2.zip + sudo ./aws/install + + - name: Verify AWS CLI Installation + run: aws --version + + - name: Install Amplify CLI + run: | + npm install -g @aws-amplify/cli + + - name: Verify Amplify CLI Installation + run: amplify --version + + add-credentials: + runs-on: ubuntu-latest + needs: install-cli + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + aws-region: ${{ secrets.AWS_REGION }} + + deploy-amplify: + runs-on: ubuntu-latest + needs: [checkout-code, add-credentials, install-cli] + steps: + - name: Fetch API URL from SSM + id: fetch-ssm + run: | + export NEXT_PUBLIC_API_BASE_URL=$(aws ssm get-parameter --name "/tasker/api/base-url" --query "Parameter.Value" --output text) + export NEXT_PUBLIC_COGNITO_USER_POOL_ID=$(aws ssm get-parameter --name "/tasker/cognito/user-pool-id" --query "Parameter.Value" --output text) + export NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID=$(aws ssm get-parameter --name "/tasker/cognito/client-id" --query "Parameter.Value" --output text) + export S3_PUBLIC_IMAGE_URL=$(aws ssm get-parameter --name "/tasker/s3/public-images-url" --query "Parameter.Value" --output text) + + echo "NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL" >> $GITHUB_ENV + echo "NEXT_PUBLIC_COGNITO_USER_POOL_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_ID" >> $GITHUB_ENV + echo "NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID" >> $GITHUB_ENV + echo "S3_PUBLIC_IMAGE_URL=$S3_PUBLIC_IMAGE_URL" >> $GITHUB_ENV + + - name: Set Amplify Environment Variables + run: | + amplify env set NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL + amplify env set NEXT_PUBLIC_COGNITO_USER_POOL_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_ID + amplify env set NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID + amplify env set S3_PUBLIC_IMAGE_URL=$S3_PUBLIC_IMAGE_URL + + - name: Deploy Amplify App + run: | + aws amplify start-deployment \ + --app-id ${{ secrets.AWS_AMPLIFY_APP_ID }} \ + --branch-name main diff --git a/tasker-client/amplify.yml b/tasker-client/amplify.yml new file mode 100644 index 0000000..7893ca6 --- /dev/null +++ b/tasker-client/amplify.yml @@ -0,0 +1,25 @@ +version: 1.0 +frontend: + phases: + preBuild: + commands: + - cd tasker-client + - npm ci + build: + commands: + - cd tasker-client + - npm run build + artifacts: + baseDirectory: tasker-client/.next + files: + - "**/*" + cache: + paths: + - tasker-client/node_modules/**/* +env: + variables: + # To be populated by GitHub Actions + NEXT_PUBLIC_API_BASE_URL: "" + NEXT_PUBLIC_COGNITO_USER_POOL_ID: "" + NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID: "" + S3_PUBLIC_IMAGE_URL: "" -- 2.49.1 From 73426fca919b19d1536fbe0ef28922852e1276c5 Mon Sep 17 00:00:00 2001 From: Andrew Trieu Date: Sat, 23 Nov 2024 18:37:51 +0200 Subject: [PATCH 2/6] feat: Update Amplify deployment workflow and add Serverless deployment workflow --- .github/workflows/amplify-deployment.yml | 4 +- .github/workflows/serverless-deployment.yml | 59 +++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/serverless-deployment.yml diff --git a/.github/workflows/amplify-deployment.yml b/.github/workflows/amplify-deployment.yml index 88c58ac..3a50393 100644 --- a/.github/workflows/amplify-deployment.yml +++ b/.github/workflows/amplify-deployment.yml @@ -38,7 +38,7 @@ jobs: - name: Verify Amplify CLI Installation run: amplify --version - add-credentials: + assume-role: runs-on: ubuntu-latest needs: install-cli steps: @@ -50,7 +50,7 @@ jobs: deploy-amplify: runs-on: ubuntu-latest - needs: [checkout-code, add-credentials, install-cli] + needs: [checkout-code, assume-role, install-cli] steps: - name: Fetch API URL from SSM id: fetch-ssm diff --git a/.github/workflows/serverless-deployment.yml b/.github/workflows/serverless-deployment.yml new file mode 100644 index 0000000..1487620 --- /dev/null +++ b/.github/workflows/serverless-deployment.yml @@ -0,0 +1,59 @@ +name: Deploy with Serverless Framework + +permissions: + id-token: write + contents: read + +on: + push: + branches: + - main + paths: + - "tasker-server/**" + +jobs: + checkout-code: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + install-deps: + runs-on: ubuntu-latest + needs: checkout-code + steps: + - name: Install AWS CLI + run: | + curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" + unzip awscliv2.zip + sudo ./aws/install + + - name: Verify AWS CLI Installation + run: aws --version + + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: "20" + + - name: Install Serverless Framework + run: npm install -g serverless + + assume-role: + runs-on: ubuntu-latest + needs: install-deps + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + aws-region: ${{ secrets.AWS_REGION }} + + deploy-serverless: + runs-on: ubuntu-latest + needs: [checkout-code, assume-role, install-deps] + steps: + - name: Deploy Serverless Application + run: | + cd tasker-server + serverless deploy -- 2.49.1 From 0e5035aab5d5ff646c29870f98bca72ce58be828 Mon Sep 17 00:00:00 2001 From: Andrew Trieu Date: Sat, 23 Nov 2024 18:41:14 +0200 Subject: [PATCH 3/6] feat: Set environment variables for Amplify monorepo deployment --- .github/workflows/amplify-deployment.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/amplify-deployment.yml b/.github/workflows/amplify-deployment.yml index 3a50393..837de51 100644 --- a/.github/workflows/amplify-deployment.yml +++ b/.github/workflows/amplify-deployment.yml @@ -71,6 +71,8 @@ jobs: amplify env set NEXT_PUBLIC_COGNITO_USER_POOL_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_ID amplify env set NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID amplify env set S3_PUBLIC_IMAGE_URL=$S3_PUBLIC_IMAGE_URL + amplify env set AMPLIFY_MONOREPO_APP_ROOT=tasker-client + amplify env set AMPLIFY_DIFF_DEPLOY=false - name: Deploy Amplify App run: | -- 2.49.1 From 29e8d3d133d4039b39aaf487f78b5d4ebe02cf4d Mon Sep 17 00:00:00 2001 From: Andrew Trieu Date: Sun, 24 Nov 2024 15:10:05 +0200 Subject: [PATCH 4/6] feat: Refactor Amplify deployment configuration for monorepo structure --- .github/workflows/amplify-deployment.yml | 4 ++-- tasker-client/amplify.yml | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/amplify-deployment.yml b/.github/workflows/amplify-deployment.yml index 837de51..8cbdee2 100644 --- a/.github/workflows/amplify-deployment.yml +++ b/.github/workflows/amplify-deployment.yml @@ -71,11 +71,11 @@ jobs: amplify env set NEXT_PUBLIC_COGNITO_USER_POOL_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_ID amplify env set NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID amplify env set S3_PUBLIC_IMAGE_URL=$S3_PUBLIC_IMAGE_URL - amplify env set AMPLIFY_MONOREPO_APP_ROOT=tasker-client - amplify env set AMPLIFY_DIFF_DEPLOY=false - name: Deploy Amplify App run: | + export AMPLIFY_MONOREPO_APP_ROOT=tasker-client + export AMPLIFY_DIFF_DEPLOY=false aws amplify start-deployment \ --app-id ${{ secrets.AWS_AMPLIFY_APP_ID }} \ --branch-name main diff --git a/tasker-client/amplify.yml b/tasker-client/amplify.yml index 7893ca6..c3a3de9 100644 --- a/tasker-client/amplify.yml +++ b/tasker-client/amplify.yml @@ -3,22 +3,23 @@ frontend: phases: preBuild: commands: - - cd tasker-client - npm ci build: commands: - - cd tasker-client - npm run build artifacts: - baseDirectory: tasker-client/.next + baseDirectory: .next files: - "**/*" cache: paths: - - tasker-client/node_modules/**/* + - node_modules/**/* + - .next/cache/**/* + - .npm/**/* env: variables: - # To be populated by GitHub Actions + AMPLIFY_MONOREPO_APP_ROOT: tasker-client + AMPLIFY_DIFF_DEPLOY: false NEXT_PUBLIC_API_BASE_URL: "" NEXT_PUBLIC_COGNITO_USER_POOL_ID: "" NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID: "" -- 2.49.1 From b7771ac827509960231c3f974c213257b8e2d220 Mon Sep 17 00:00:00 2001 From: Andrew Trieu Date: Tue, 26 Nov 2024 10:34:29 +0200 Subject: [PATCH 5/6] feat: Update environment variable naming and refactor image URL handling across the application --- .github/workflows/amplify-deployment.yml | 6 +- tasker-client/amplify.yml | 2 +- tasker-client/next.config.ts | 12 ++- .../src/app/components/ModalNewTask/index.tsx | 76 +++++++++++-------- .../src/app/components/Navbar/index.tsx | 2 +- .../src/app/components/Sidebar/index.tsx | 4 +- .../src/app/components/TaskCard/index.tsx | 2 +- .../src/app/components/UserCard/index.tsx | 2 +- .../src/app/projects/BoardView/index.tsx | 12 +-- .../src/app/projects/TimelineView/index.tsx | 45 ++++++----- tasker-client/src/app/users/page.tsx | 2 +- tasker-server/seed/populateSeedData.ts | 2 +- tasker-server/src/handlers/createUser.ts | 3 +- tasker-server/src/handlers/getUserTasks.ts | 6 +- 14 files changed, 107 insertions(+), 69 deletions(-) diff --git a/.github/workflows/amplify-deployment.yml b/.github/workflows/amplify-deployment.yml index 8cbdee2..f36b18e 100644 --- a/.github/workflows/amplify-deployment.yml +++ b/.github/workflows/amplify-deployment.yml @@ -58,19 +58,19 @@ jobs: export NEXT_PUBLIC_API_BASE_URL=$(aws ssm get-parameter --name "/tasker/api/base-url" --query "Parameter.Value" --output text) export NEXT_PUBLIC_COGNITO_USER_POOL_ID=$(aws ssm get-parameter --name "/tasker/cognito/user-pool-id" --query "Parameter.Value" --output text) export NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID=$(aws ssm get-parameter --name "/tasker/cognito/client-id" --query "Parameter.Value" --output text) - export S3_PUBLIC_IMAGE_URL=$(aws ssm get-parameter --name "/tasker/s3/public-images-url" --query "Parameter.Value" --output text) + export NEXT_PUBLIC_S3_PUBLIC_IMAGE_URL=$(aws ssm get-parameter --name "/tasker/s3/public-images-url" --query "Parameter.Value" --output text) echo "NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL" >> $GITHUB_ENV echo "NEXT_PUBLIC_COGNITO_USER_POOL_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_ID" >> $GITHUB_ENV echo "NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID" >> $GITHUB_ENV - echo "S3_PUBLIC_IMAGE_URL=$S3_PUBLIC_IMAGE_URL" >> $GITHUB_ENV + echo "NEXT_PUBLIC_S3_PUBLIC_IMAGE_URL=$NEXT_PUBLIC_S3_PUBLIC_IMAGE_URL" >> $GITHUB_ENV - name: Set Amplify Environment Variables run: | amplify env set NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL amplify env set NEXT_PUBLIC_COGNITO_USER_POOL_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_ID amplify env set NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID=$NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID - amplify env set S3_PUBLIC_IMAGE_URL=$S3_PUBLIC_IMAGE_URL + amplify env set NEXT_PUBLIC_S3_PUBLIC_IMAGE_URL=$NEXT_PUBLIC_S3_PUBLIC_IMAGE_URL - name: Deploy Amplify App run: | diff --git a/tasker-client/amplify.yml b/tasker-client/amplify.yml index c3a3de9..3145809 100644 --- a/tasker-client/amplify.yml +++ b/tasker-client/amplify.yml @@ -23,4 +23,4 @@ env: NEXT_PUBLIC_API_BASE_URL: "" NEXT_PUBLIC_COGNITO_USER_POOL_ID: "" NEXT_PUBLIC_COGNITO_USER_POOL_CLIENT_ID: "" - S3_PUBLIC_IMAGE_URL: "" + NEXT_PUBLIC_S3_PUBLIC_IMAGE_URL: "" diff --git a/tasker-client/next.config.ts b/tasker-client/next.config.ts index e9ffa30..eede2b9 100644 --- a/tasker-client/next.config.ts +++ b/tasker-client/next.config.ts @@ -1,7 +1,17 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + images: { + remotePatterns: [ + { + protocol: "https", + hostname: new URL(process.env.NEXT_PUBLIC_S3_PUBLIC_IMAGE_URL || "") + .hostname, + port: "", + pathname: "/**", + }, + ], + }, }; export default nextConfig; diff --git a/tasker-client/src/app/components/ModalNewTask/index.tsx b/tasker-client/src/app/components/ModalNewTask/index.tsx index 9748383..0631291 100644 --- a/tasker-client/src/app/components/ModalNewTask/index.tsx +++ b/tasker-client/src/app/components/ModalNewTask/index.tsx @@ -1,6 +1,11 @@ import Modal from "@/app/components/Modal"; -import { Priority, Status, useCreateTaskMutation } from "@/state/api"; -import React, { useState } from "react"; +import { + Priority, + Status, + useCreateTaskMutation, + useGetAuthUserQuery, +} from "@/state/api"; +import React, { useEffect, useState } from "react"; import { formatISO } from "date-fns"; type Props = { @@ -22,22 +27,28 @@ const ModalNewTask = ({ isOpen, onClose, id = null }: Props) => { const [assignedUserId, setAssignedUserId] = useState(""); const [projectId, setProjectId] = useState(""); + const { data: currentUser } = useGetAuthUserQuery({}); + const userId = currentUser?.userDetails?.userId ?? null; + + useEffect(() => { + setAuthorUserId(userId || ""); + }, [userId]); + const handleSubmit = async () => { - console.log(title, authorUserId, id, projectId); + if (!(title && authorUserId && (id !== null || projectId))) return; - console.log("Creating task 1.."); - if ( - !(title && authorUserId && assignedUserId && (id !== null || projectId)) - ) - return; - console.log("Creating task 2..."); + const finalAssignedUserId = assignedUserId.trim() || authorUserId; - const formattedStartDate = formatISO(new Date(startDate), { - representation: "complete", - }); - const formattedDueDate = formatISO(new Date(dueDate), { - representation: "complete", - }); + const formattedStartDate = + startDate ?? + formatISO(new Date(startDate), { + representation: "complete", + }); + const formattedDueDate = + dueDate ?? + formatISO(new Date(dueDate), { + representation: "complete", + }); await createTask({ title, @@ -48,16 +59,15 @@ const ModalNewTask = ({ isOpen, onClose, id = null }: Props) => { startDate: formattedStartDate, dueDate: formattedDueDate, authorUserId: authorUserId, - assignedUserId: assignedUserId, + assignedUserId: finalAssignedUserId, projectId: id !== null ? id : projectId, }); + + onClose(); }; const isFormValid = () => { - console.log(title, authorUserId, id, projectId); - return ( - title && authorUserId && assignedUserId && (id !== null || projectId) - ); + return title && authorUserId && (id !== null || projectId); }; const selectStyles = @@ -92,9 +102,13 @@ const ModalNewTask = ({ isOpen, onClose, id = null }: Props) => { setAuthorUserId(e.target.value)} - /> + {authorUserId === "" && ( + setAuthorUserId(e.target.value)} + /> + )} {
{!!currentUserDetails?.profilePictureUrl ? ( {currentUserDetails?.username {
logo {
{!!currentUserDetails?.profilePictureUrl ? ( {currentUserDetails?.username {
{task.attachments && task.attachments.length > 0 && ( {task.attachments[0].fileName} {
{user.profilePictureUrl && ( profile picture { data: fetchedTasks, isLoading, error, + refetch, } = useGetTasksQuery({ projectId }); const [updateTaskStatus] = useUpdateTaskStatusMutation(); const [tasks, setTasks] = useState([]); @@ -43,6 +44,7 @@ const BoardView = ({ projectId, setIsModalNewTaskOpen }: BoardProps) => { try { await updateTaskStatus({ taskId, status: toStatus }); + await refetch(); } catch (error) { console.error("Failed to update task status:", error); setTasks(fetchedTasks || []); @@ -197,7 +199,7 @@ const Task = ({ task }: TaskProps) => { > {task.attachments && task.attachments.length > 0 && ( {task.attachments[0].fileName} {
{task.assignee && ( {task.assignee.username} { )} {task.author && ( {task.author.username} { }); const ganttTasks = useMemo(() => { + if (!tasks || tasks.length === 0) return []; return ( - tasks?.map((task) => ({ - start: new Date(task.startDate as string), - end: new Date(task.dueDate as string), - name: task.title, - id: `Task-${task.taskId}`, - type: "task" as TaskTypeItems, - progress: task.points ? (task.points / 10) * 100 : 0, - isDisabled: false, - })) || [] + tasks + ?.filter((task) => task.startDate && task.dueDate) + .map((task) => ({ + start: new Date(task.startDate as string), + end: new Date(task.dueDate as string), + name: task.title, + id: `Task-${task.taskId}`, + type: "task" as TaskTypeItems, + progress: task.points ? (task.points / 10) * 100 : 0, + isDisabled: false, + })) || [] ); }, [tasks]); @@ -66,16 +69,20 @@ const Timeline = ({ projectId, setIsModalNewTaskOpen }: Props) => {
-
- -
+ {ganttTasks.length > 0 && ( +
+ +
+ )}