From 257a99ffae357aa0e7073e10015918f9000b63e0 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Tue, 31 Dec 2024 15:44:36 +0530 Subject: [PATCH 01/49] Working on Github Actions. --- .../.github/workflows/app-deployment.yml | 29 +++++++++++++++++++ .../.github/workflows/clone-and-setup.yml | 28 ++++++++++++++++++ .../.github/workflows/db-operations.yml | 21 ++++++++++++++ deeplink-generator/.gitignore | 2 ++ deeplink-generator/docker-compose.dev.yml | 25 ++++++++++++++++ ...r-compose.yml => docker-compose.local.yml} | 0 .../src/app/components/UsecaseEditor.tsx | 2 +- .../src/app/resolver/[deepLinkId]/route.ts | 3 +- 8 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 deeplink-generator/.github/workflows/app-deployment.yml create mode 100644 deeplink-generator/.github/workflows/clone-and-setup.yml create mode 100644 deeplink-generator/.github/workflows/db-operations.yml create mode 100644 deeplink-generator/docker-compose.dev.yml rename deeplink-generator/{docker-compose.yml => docker-compose.local.yml} (100%) diff --git a/deeplink-generator/.github/workflows/app-deployment.yml b/deeplink-generator/.github/workflows/app-deployment.yml new file mode 100644 index 0000000..bf56181 --- /dev/null +++ b/deeplink-generator/.github/workflows/app-deployment.yml @@ -0,0 +1,29 @@ +name: App Deployment +on: + workflow_dispatch: + +jobs: + deploy-app: + runs-on: ubuntu-latest + steps: + - name: Deploy Application + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EC2_HOST }} + username: ec2-user + key: ${{ secrets.EC2_PEM }} + port: ${{ secrets.EC2_PORT }} + script: | + cd /path/to/app + rm -f .env + git pull origin master + echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env + echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env + echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env + echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env + + rm -f docker-compose.yml + cp docker-compose.dev.yml docker-compose.yml + docker-compose build ondc_deep_link_app + docker-compose up -d ondc_deep_link_app + docker system prune -f diff --git a/deeplink-generator/.github/workflows/clone-and-setup.yml b/deeplink-generator/.github/workflows/clone-and-setup.yml new file mode 100644 index 0000000..7f5c981 --- /dev/null +++ b/deeplink-generator/.github/workflows/clone-and-setup.yml @@ -0,0 +1,28 @@ +name: Clone and Setup +on: + workflow_dispatch: + +jobs: + setup-repo: + runs-on: ubuntu-latest + steps: + - name: Setup Repository + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EC2_HOST }} + username: ec2-user + key: ${{ secrets.EC2_PEM }} + port: ${{ secrets.EC2_PORT }} + script: | + REPO_DIR="/path/to/repo" + [ -d "$REPO_DIR" ] && rm -rf "$REPO_DIR" + git clone ${{ secrets.REPO_URL }} "$REPO_DIR" + cd "$REPO_DIR" + + # Create .env file from secrets + echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env + echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env + echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env + echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env + + npm install diff --git a/deeplink-generator/.github/workflows/db-operations.yml b/deeplink-generator/.github/workflows/db-operations.yml new file mode 100644 index 0000000..014928b --- /dev/null +++ b/deeplink-generator/.github/workflows/db-operations.yml @@ -0,0 +1,21 @@ +name: Database Operations +on: + workflow_dispatch: + +jobs: + db-reset-and-migrate: + runs-on: ubuntu-latest + steps: + - name: Execute DB Operations + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EC2_HOST }} + username: ec2-user + key: ${{ secrets.EC2_PEM }} + port: ${{ secrets.EC2_PORT }} + script: | + docker-compose down ondc_deep_link_db + docker rm -f ondc_deep_link_db + docker-compose up -d ondc_deep_link_db + npx prisma migrate deploy + node seeding/seed.js diff --git a/deeplink-generator/.gitignore b/deeplink-generator/.gitignore index d32cc78..6232615 100644 --- a/deeplink-generator/.gitignore +++ b/deeplink-generator/.gitignore @@ -38,3 +38,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts +docker-compose.yml + diff --git a/deeplink-generator/docker-compose.dev.yml b/deeplink-generator/docker-compose.dev.yml new file mode 100644 index 0000000..beafea5 --- /dev/null +++ b/deeplink-generator/docker-compose.dev.yml @@ -0,0 +1,25 @@ +services: + ondc_deep_link_app: + build: . + ports: + - "8080:3000" + env_file: + - ./.env + depends_on: + - ondc_deep_link_db + + ondc_deep_link_db: + image: postgres + restart: always + # set shared memory limit when using docker-compose + shm_size: 128mb + # or set shared memory limit when deploy via swarm stack + #volumes: + # - type: tmpfs + # target: /dev/shm + # tmpfs: + # size: 134217728 # 128*2^20 bytes = 128Mb + ports: + - "5432:5432" + env_file: + - ./.env diff --git a/deeplink-generator/docker-compose.yml b/deeplink-generator/docker-compose.local.yml similarity index 100% rename from deeplink-generator/docker-compose.yml rename to deeplink-generator/docker-compose.local.yml diff --git a/deeplink-generator/src/app/components/UsecaseEditor.tsx b/deeplink-generator/src/app/components/UsecaseEditor.tsx index 1943e16..6772cd9 100644 --- a/deeplink-generator/src/app/components/UsecaseEditor.tsx +++ b/deeplink-generator/src/app/components/UsecaseEditor.tsx @@ -104,7 +104,7 @@ export const UsecaseEditor = ({ usecase }: UsecaseEditorProps) => { .map((key: string) => ( - + {typeof flattenedTemplate[key] === "string" ? ( diff --git a/deeplink-generator/src/app/resolver/[deepLinkId]/route.ts b/deeplink-generator/src/app/resolver/[deepLinkId]/route.ts index 3788bca..c7baeb6 100644 --- a/deeplink-generator/src/app/resolver/[deepLinkId]/route.ts +++ b/deeplink-generator/src/app/resolver/[deepLinkId]/route.ts @@ -6,6 +6,7 @@ export async function GET( { params }: { params: Promise<{ deepLinkId: string }> } ) { const deepLinkId = (await params).deepLinkId; - const deepLink = await getUsecaseById(deepLinkId); + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any + const {template ,...deepLink} = (await getUsecaseById(deepLinkId)) as any; return NextResponse.json(deepLink); } From 401a0d2a560246e4fe55fd8e356e46f000412cef Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 12:46:51 +0530 Subject: [PATCH 02/49] Working on Deployment. --- .../.github/workflows/app-deployment.yml | 14 +++++++---- .../.github/workflows/clone-and-setup.yml | 2 +- deeplink-generator/nginx/nginx.conf | 25 +++++++------------ 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/deeplink-generator/.github/workflows/app-deployment.yml b/deeplink-generator/.github/workflows/app-deployment.yml index bf56181..26bd6e1 100644 --- a/deeplink-generator/.github/workflows/app-deployment.yml +++ b/deeplink-generator/.github/workflows/app-deployment.yml @@ -14,16 +14,20 @@ jobs: key: ${{ secrets.EC2_PEM }} port: ${{ secrets.EC2_PORT }} script: | - cd /path/to/app + cd "deep-link-repo" rm -f .env + rm -f docker-compose.yml + git pull origin master + echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env - rm -f docker-compose.yml + cp docker-compose.dev.yml docker-compose.yml - docker-compose build ondc_deep_link_app - docker-compose up -d ondc_deep_link_app - docker system prune -f + sudo docker-compose up ondc_deep_link_app -d --build + sudo docker system prune -f + sudo systemctl restart nginx + diff --git a/deeplink-generator/.github/workflows/clone-and-setup.yml b/deeplink-generator/.github/workflows/clone-and-setup.yml index 7f5c981..bd96245 100644 --- a/deeplink-generator/.github/workflows/clone-and-setup.yml +++ b/deeplink-generator/.github/workflows/clone-and-setup.yml @@ -14,7 +14,7 @@ jobs: key: ${{ secrets.EC2_PEM }} port: ${{ secrets.EC2_PORT }} script: | - REPO_DIR="/path/to/repo" + REPO_DIR="deep-link-repo" [ -d "$REPO_DIR" ] && rm -rf "$REPO_DIR" git clone ${{ secrets.REPO_URL }} "$REPO_DIR" cd "$REPO_DIR" diff --git a/deeplink-generator/nginx/nginx.conf b/deeplink-generator/nginx/nginx.conf index 0415c5b..41f2441 100644 --- a/deeplink-generator/nginx/nginx.conf +++ b/deeplink-generator/nginx/nginx.conf @@ -4,25 +4,15 @@ http { upstream nextjs_upstream { - server host.docker.internal:8080; + server localhost:8080; } server { listen 80; listen [::]:80; - server_name _; + server_name deeplink.resolver.ondc.org; - location / { - proxy_pass http://nextjs_upstream; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } + return 301 https://$server_name$request_uri; } server { @@ -30,9 +20,8 @@ http { listen [::]:443 ssl; server_name _; - ssl_certificate /etc/nginx/ssl/cert.pem; - ssl_certificate_key /etc/nginx/ssl/key.pem; - + ssl_certificate /etc/letsencrypt/live/deeplink.resolver.ondc.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/deeplink.resolver.ondc.org/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; @@ -49,3 +38,7 @@ http { } } } + +add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; +add_header X-Frame-Options DENY; +add_header X-Content-Type-Options nosniff; From 163ecbe734ee7095d2ca0d0950b44efe78407231 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 13:07:16 +0530 Subject: [PATCH 03/49] Fix: Github Actions. --- .../.github => .github}/workflows/app-deployment.yml | 5 +++-- .../.github => .github}/workflows/clone-and-setup.yml | 2 ++ .../.github => .github}/workflows/db-operations.yml | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) rename {deeplink-generator/.github => .github}/workflows/app-deployment.yml (90%) rename {deeplink-generator/.github => .github}/workflows/clone-and-setup.yml (96%) rename {deeplink-generator/.github => .github}/workflows/db-operations.yml (85%) diff --git a/deeplink-generator/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml similarity index 90% rename from deeplink-generator/.github/workflows/app-deployment.yml rename to .github/workflows/app-deployment.yml index 26bd6e1..414ed87 100644 --- a/deeplink-generator/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -6,7 +6,7 @@ jobs: deploy-app: runs-on: ubuntu-latest steps: - - name: Deploy Application + - name: Deploy Generator App uses: appleboy/ssh-action@master with: host: ${{ secrets.EC2_HOST }} @@ -14,11 +14,12 @@ jobs: key: ${{ secrets.EC2_PEM }} port: ${{ secrets.EC2_PORT }} script: | - cd "deep-link-repo" + cd deep-link-repo rm -f .env rm -f docker-compose.yml git pull origin master + cd deeplink-generator echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env diff --git a/deeplink-generator/.github/workflows/clone-and-setup.yml b/.github/workflows/clone-and-setup.yml similarity index 96% rename from deeplink-generator/.github/workflows/clone-and-setup.yml rename to .github/workflows/clone-and-setup.yml index bd96245..7cca177 100644 --- a/deeplink-generator/.github/workflows/clone-and-setup.yml +++ b/.github/workflows/clone-and-setup.yml @@ -18,6 +18,8 @@ jobs: [ -d "$REPO_DIR" ] && rm -rf "$REPO_DIR" git clone ${{ secrets.REPO_URL }} "$REPO_DIR" cd "$REPO_DIR" + + cd deeplink-generator # Create .env file from secrets echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env diff --git a/deeplink-generator/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml similarity index 85% rename from deeplink-generator/.github/workflows/db-operations.yml rename to .github/workflows/db-operations.yml index 014928b..bd503e0 100644 --- a/deeplink-generator/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -6,7 +6,7 @@ jobs: db-reset-and-migrate: runs-on: ubuntu-latest steps: - - name: Execute DB Operations + - name: Execute Generator DB Operations uses: appleboy/ssh-action@master with: host: ${{ secrets.EC2_HOST }} @@ -14,6 +14,7 @@ jobs: key: ${{ secrets.EC2_PEM }} port: ${{ secrets.EC2_PORT }} script: | + cd deep-link-repo/deeplink-generator docker-compose down ondc_deep_link_db docker rm -f ondc_deep_link_db docker-compose up -d ondc_deep_link_db From ef3a1e4ee8fc16ce2ffaa311ba99c4a66c10971b Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 14:27:00 +0530 Subject: [PATCH 04/49] Fixed username. --- .github/workflows/app-deployment.yml | 4 ++-- .github/workflows/clone-and-setup.yml | 4 ++-- .github/workflows/db-operations.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 414ed87..27a18fe 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -10,9 +10,9 @@ jobs: uses: appleboy/ssh-action@master with: host: ${{ secrets.EC2_HOST }} - username: ec2-user + username: ${{ secrets.EC2_USERNAME }} key: ${{ secrets.EC2_PEM }} - port: ${{ secrets.EC2_PORT }} + port: 22 script: | cd deep-link-repo rm -f .env diff --git a/.github/workflows/clone-and-setup.yml b/.github/workflows/clone-and-setup.yml index 7cca177..96109d5 100644 --- a/.github/workflows/clone-and-setup.yml +++ b/.github/workflows/clone-and-setup.yml @@ -10,9 +10,9 @@ jobs: uses: appleboy/ssh-action@master with: host: ${{ secrets.EC2_HOST }} - username: ec2-user + username: ${{ secrets.EC2_USERNAME }} key: ${{ secrets.EC2_PEM }} - port: ${{ secrets.EC2_PORT }} + port: 22 script: | REPO_DIR="deep-link-repo" [ -d "$REPO_DIR" ] && rm -rf "$REPO_DIR" diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index bd503e0..01945d9 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -10,9 +10,9 @@ jobs: uses: appleboy/ssh-action@master with: host: ${{ secrets.EC2_HOST }} - username: ec2-user + username: ${{ secrets.EC2_USERNAME }} key: ${{ secrets.EC2_PEM }} - port: ${{ secrets.EC2_PORT }} + port: 22 script: | cd deep-link-repo/deeplink-generator docker-compose down ondc_deep_link_db From 68e9112f5dbff7d8897e07587f37ea1eb59bde24 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 14:29:52 +0530 Subject: [PATCH 05/49] Working on deployment. --- .github/workflows/app-deployment.yml | 8 +++++--- .github/workflows/clone-and-setup.yml | 9 +++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 27a18fe..21d5620 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -14,12 +14,14 @@ jobs: key: ${{ secrets.EC2_PEM }} port: 22 script: | - cd deep-link-repo + cd deeplink-resolver-server + + git pull origin master + cd deeplink-generator + rm -f .env rm -f docker-compose.yml - git pull origin master - cd deeplink-generator echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env diff --git a/.github/workflows/clone-and-setup.yml b/.github/workflows/clone-and-setup.yml index 96109d5..8b8c98d 100644 --- a/.github/workflows/clone-and-setup.yml +++ b/.github/workflows/clone-and-setup.yml @@ -14,12 +14,9 @@ jobs: key: ${{ secrets.EC2_PEM }} port: 22 script: | - REPO_DIR="deep-link-repo" - [ -d "$REPO_DIR" ] && rm -rf "$REPO_DIR" - git clone ${{ secrets.REPO_URL }} "$REPO_DIR" - cd "$REPO_DIR" - - cd deeplink-generator + [ -d "deeplink-resolver-server" ] && rm -rf "deeplink-resolver-server" + git clone git@github.com:abhik-wil/deeplink-resolver-server.git + cd deeplink-resolver-server/deeplink-generator # Create .env file from secrets echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env From 490c32ed9f8a599e5162bbb5c7e09fe9386ec019 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:29:16 +0530 Subject: [PATCH 06/49] Update clone-and-setup.yml --- .github/workflows/clone-and-setup.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/clone-and-setup.yml b/.github/workflows/clone-and-setup.yml index 8b8c98d..b7e5522 100644 --- a/.github/workflows/clone-and-setup.yml +++ b/.github/workflows/clone-and-setup.yml @@ -14,6 +14,7 @@ jobs: key: ${{ secrets.EC2_PEM }} port: 22 script: | + echo "STARTING Deployment" [ -d "deeplink-resolver-server" ] && rm -rf "deeplink-resolver-server" git clone git@github.com:abhik-wil/deeplink-resolver-server.git cd deeplink-resolver-server/deeplink-generator From 99db7220a86f61657bc5732e22039b0c78d6acd5 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:30:20 +0530 Subject: [PATCH 07/49] Update clone-and-setup.yml --- .github/workflows/clone-and-setup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clone-and-setup.yml b/.github/workflows/clone-and-setup.yml index b7e5522..d6221b0 100644 --- a/.github/workflows/clone-and-setup.yml +++ b/.github/workflows/clone-and-setup.yml @@ -16,7 +16,7 @@ jobs: script: | echo "STARTING Deployment" [ -d "deeplink-resolver-server" ] && rm -rf "deeplink-resolver-server" - git clone git@github.com:abhik-wil/deeplink-resolver-server.git + sudo git clone git@github.com:abhik-wil/deeplink-resolver-server.git cd deeplink-resolver-server/deeplink-generator # Create .env file from secrets From 21740d942c5949254f84ba413e8a306c36e79716 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:45:10 +0530 Subject: [PATCH 08/49] Update clone-and-setup.yml --- .github/workflows/clone-and-setup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clone-and-setup.yml b/.github/workflows/clone-and-setup.yml index d6221b0..33475ae 100644 --- a/.github/workflows/clone-and-setup.yml +++ b/.github/workflows/clone-and-setup.yml @@ -16,7 +16,7 @@ jobs: script: | echo "STARTING Deployment" [ -d "deeplink-resolver-server" ] && rm -rf "deeplink-resolver-server" - sudo git clone git@github.com:abhik-wil/deeplink-resolver-server.git + sudo git clone https://github.com/abhik-wil/deeplink-resolver-server.git cd deeplink-resolver-server/deeplink-generator # Create .env file from secrets From 0b8018142e0207f2940f05726779c07a9a3b7c3e Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 15:56:01 +0530 Subject: [PATCH 09/49] sanity commit. --- .github/workflows/app-deployment.yml | 24 ++++++++++++++--------- .github/workflows/clone-and-setup.yml | 28 --------------------------- .github/workflows/db-operations.yml | 6 ++++++ 3 files changed, 21 insertions(+), 37 deletions(-) delete mode 100644 .github/workflows/clone-and-setup.yml diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 21d5620..d190a8d 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -1,5 +1,9 @@ name: App Deployment on: + push: + branches: [ master ] + pull_request: + branches: [ master ] workflow_dispatch: jobs: @@ -14,19 +18,21 @@ jobs: key: ${{ secrets.EC2_PEM }} port: 22 script: | - cd deeplink-resolver-server - - git pull origin master - cd deeplink-generator + echo "STARTING Deployment" + [ -d "deeplink-resolver-server" ] && rm -rf "deeplink-resolver-server" + sudo git clone https://github.com/abhik-wil/deeplink-resolver-server.git + cd deeplink-resolver-server/deeplink-generator rm -f .env rm -f docker-compose.yml - - echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env - echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env - echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env - echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env + # Create backend .env file + cat > .env << EOL + DATABASE_URL=${{ secrets.DATABASE_URL }} + POSTGRES_USER=${{ secrets.POSTGRES_USER }} + POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} + POSTGRES_DB=${{ secrets.POSTGRES_DB }} + EOL cp docker-compose.dev.yml docker-compose.yml diff --git a/.github/workflows/clone-and-setup.yml b/.github/workflows/clone-and-setup.yml deleted file mode 100644 index 33475ae..0000000 --- a/.github/workflows/clone-and-setup.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Clone and Setup -on: - workflow_dispatch: - -jobs: - setup-repo: - runs-on: ubuntu-latest - steps: - - name: Setup Repository - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.EC2_HOST }} - username: ${{ secrets.EC2_USERNAME }} - key: ${{ secrets.EC2_PEM }} - port: 22 - script: | - echo "STARTING Deployment" - [ -d "deeplink-resolver-server" ] && rm -rf "deeplink-resolver-server" - sudo git clone https://github.com/abhik-wil/deeplink-resolver-server.git - cd deeplink-resolver-server/deeplink-generator - - # Create .env file from secrets - echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env - echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env - echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env - echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env - - npm install diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index 01945d9..8094518 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -15,8 +15,14 @@ jobs: port: 22 script: | cd deep-link-repo/deeplink-generator + + npm i --force + docker-compose down ondc_deep_link_db docker rm -f ondc_deep_link_db docker-compose up -d ondc_deep_link_db + + sleep 10 + npx prisma migrate deploy node seeding/seed.js From e965288e67aa165dc52d38658b2c97535a67e3bd Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 15:58:32 +0530 Subject: [PATCH 10/49] snaity commit. --- .github/workflows/app-deployment.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index d190a8d..053e5f4 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -19,15 +19,15 @@ jobs: port: 22 script: | echo "STARTING Deployment" - [ -d "deeplink-resolver-server" ] && rm -rf "deeplink-resolver-server" + [ -d "deeplink-resolver-server" ] && sudo rm -rf "deeplink-resolver-server" sudo git clone https://github.com/abhik-wil/deeplink-resolver-server.git - cd deeplink-resolver-server/deeplink-generator + sudo cd deeplink-resolver-server/deeplink-generator - rm -f .env - rm -f docker-compose.yml + sudo rm -f .env + sudo rm -f docker-compose.yml # Create backend .env file - cat > .env << EOL + sudo cat > .env << EOL DATABASE_URL=${{ secrets.DATABASE_URL }} POSTGRES_USER=${{ secrets.POSTGRES_USER }} POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} @@ -35,7 +35,7 @@ jobs: EOL - cp docker-compose.dev.yml docker-compose.yml + sudo cp docker-compose.dev.yml docker-compose.yml sudo docker-compose up ondc_deep_link_app -d --build sudo docker system prune -f sudo systemctl restart nginx From 3a28e3233fdfc1c7d2bccff0126d6bf161df758d Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 16:00:42 +0530 Subject: [PATCH 11/49] s. --- .github/workflows/app-deployment.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 053e5f4..7ec774c 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -21,7 +21,7 @@ jobs: echo "STARTING Deployment" [ -d "deeplink-resolver-server" ] && sudo rm -rf "deeplink-resolver-server" sudo git clone https://github.com/abhik-wil/deeplink-resolver-server.git - sudo cd deeplink-resolver-server/deeplink-generator + cd deeplink-resolver-server/deeplink-generator sudo rm -f .env sudo rm -f docker-compose.yml @@ -36,7 +36,7 @@ jobs: sudo cp docker-compose.dev.yml docker-compose.yml - sudo docker-compose up ondc_deep_link_app -d --build + sudo docker compose up ondc_deep_link_app -d --build sudo docker system prune -f sudo systemctl restart nginx From 2c81c4a8ffa5dff3df3e6b6ab9bcb0487a07f72b Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 16:02:18 +0530 Subject: [PATCH 12/49] sc. --- .github/workflows/app-deployment.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 7ec774c..3c8f3b3 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -23,8 +23,6 @@ jobs: sudo git clone https://github.com/abhik-wil/deeplink-resolver-server.git cd deeplink-resolver-server/deeplink-generator - sudo rm -f .env - sudo rm -f docker-compose.yml # Create backend .env file sudo cat > .env << EOL From 136a005983ee8b284da86128aad55d4283746e80 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 16:30:36 +0530 Subject: [PATCH 13/49] logging. --- .github/workflows/app-deployment.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 3c8f3b3..e075a41 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -24,8 +24,9 @@ jobs: cd deeplink-resolver-server/deeplink-generator + echo "STARTING Deployment" # Create backend .env file - sudo cat > .env << EOL + cat > .env << EOL DATABASE_URL=${{ secrets.DATABASE_URL }} POSTGRES_USER=${{ secrets.POSTGRES_USER }} POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} @@ -33,8 +34,13 @@ jobs: EOL + echo "STARTING build" sudo cp docker-compose.dev.yml docker-compose.yml sudo docker compose up ondc_deep_link_app -d --build sudo docker system prune -f + + + echo "BUILD Complete; Restarting Nginx" sudo systemctl restart nginx + echo "Deployment COMPLETE" From 4b373fef2b663b6e7a3dbced52eec83831fc4b44 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:45:55 +0530 Subject: [PATCH 14/49] Update app-deployment.yml --- .github/workflows/app-deployment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index e075a41..7d1aa0b 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -25,6 +25,7 @@ jobs: echo "STARTING Deployment" + touch .env # Create backend .env file cat > .env << EOL DATABASE_URL=${{ secrets.DATABASE_URL }} From 8d4ec0490617dee5370e2b381fddc062e98155b6 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:47:37 +0530 Subject: [PATCH 15/49] Update app-deployment.yml --- .github/workflows/app-deployment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 7d1aa0b..8f89370 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -25,7 +25,7 @@ jobs: echo "STARTING Deployment" - touch .env + sudo touch .env # Create backend .env file cat > .env << EOL DATABASE_URL=${{ secrets.DATABASE_URL }} From fd4adf9f4592f5b382ce1fb8484b77754222a662 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:52:17 +0530 Subject: [PATCH 16/49] Update db-operations.yml --- .github/workflows/db-operations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index 8094518..6cf2c70 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -14,7 +14,7 @@ jobs: key: ${{ secrets.EC2_PEM }} port: 22 script: | - cd deep-link-repo/deeplink-generator + cd deeplink-resolver-server/deeplink-generator npm i --force From 53fd13f7b642009e8b8698b4955973c01fea2408 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:54:08 +0530 Subject: [PATCH 17/49] Update db-operations.yml --- .github/workflows/db-operations.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index 6cf2c70..b8c6b66 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -18,11 +18,12 @@ jobs: npm i --force - docker-compose down ondc_deep_link_db - docker rm -f ondc_deep_link_db - docker-compose up -d ondc_deep_link_db + sudo docker-compose down ondc_deep_link_db + sudo docker rm -f ondc_deep_link_db + sudo docker-compose up -d ondc_deep_link_db sleep 10 + npx prisma migrate deploy node seeding/seed.js From a0ee0a40db78b3edba8a909a6bdf7e67c5e6009b Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:58:22 +0530 Subject: [PATCH 18/49] Update db-operations.yml --- .github/workflows/db-operations.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index b8c6b66..ac400fe 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -18,12 +18,12 @@ jobs: npm i --force - sudo docker-compose down ondc_deep_link_db + sudo docker compose down ondc_deep_link_db sudo docker rm -f ondc_deep_link_db - sudo docker-compose up -d ondc_deep_link_db + sudo docker compose up -d ondc_deep_link_db sleep 10 - + npx prisma generate npx prisma migrate deploy node seeding/seed.js From a4ec0f7254b763ab15fcac0b86435d6612f2e285 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:09:11 +0530 Subject: [PATCH 19/49] Update db-operations.yml --- .github/workflows/db-operations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index ac400fe..c9b3cea 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -16,7 +16,7 @@ jobs: script: | cd deeplink-resolver-server/deeplink-generator - npm i --force + sudo npm i --force sudo docker compose down ondc_deep_link_db sudo docker rm -f ondc_deep_link_db From 32a51d7b9e11ff25382d4562c9e782b5e2a943e3 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:16:38 +0530 Subject: [PATCH 20/49] Update db-operations.yml --- .github/workflows/db-operations.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index c9b3cea..0d70c01 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -15,9 +15,13 @@ jobs: port: 22 script: | cd deeplink-resolver-server/deeplink-generator + node -v + npm -v + echo "STARTING Dep Installation" sudo npm i --force + echo "DB Teardown and Restart" sudo docker compose down ondc_deep_link_db sudo docker rm -f ondc_deep_link_db sudo docker compose up -d ondc_deep_link_db From e9a09f1edd288c0485c1a7d42a96b45a18532cfd Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 17:17:45 +0530 Subject: [PATCH 21/49] Removing unused classes. --- .../deep-link/usecases/thank-you/[deepLinkId]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/thank-you/[deepLinkId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/thank-you/[deepLinkId]/page.tsx index 190e7d3..731be92 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/thank-you/[deepLinkId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/thank-you/[deepLinkId]/page.tsx @@ -7,7 +7,7 @@ import { import { Paper, Typography, Grid2 as Grid, Box } from "@mui/material"; import Link from "next/link"; import React from "react"; -import Divider, { dividerClasses } from '@mui/material/Divider'; +import Divider from '@mui/material/Divider'; const DeepLinkThankYouPage = async ({ params, From 4e50cb55c13ba137c3b86a66f60e0676e15cba3d Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:22:55 +0530 Subject: [PATCH 22/49] Update db-operations.yml --- .github/workflows/db-operations.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index 0d70c01..8b2ed50 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -15,6 +15,7 @@ jobs: port: 22 script: | cd deeplink-resolver-server/deeplink-generator + nvm use --lts node -v npm -v From 3235bcf344db380e677cd8010ea239bf2308aba5 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:52:02 +0530 Subject: [PATCH 23/49] Update db-operations.yml --- .github/workflows/db-operations.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index 8b2ed50..7910678 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -15,6 +15,11 @@ jobs: port: 22 script: | cd deeplink-resolver-server/deeplink-generator + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" + source ~/.bashrc + nvm use --lts node -v npm -v From f2496cd41807e0bdb47183c1c2db43f00106409a Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:55:53 +0530 Subject: [PATCH 24/49] Update app-deployment.yml --- .github/workflows/app-deployment.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 8f89370..67c042b 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -27,12 +27,10 @@ jobs: echo "STARTING Deployment" sudo touch .env # Create backend .env file - cat > .env << EOL - DATABASE_URL=${{ secrets.DATABASE_URL }} - POSTGRES_USER=${{ secrets.POSTGRES_USER }} - POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} - POSTGRES_DB=${{ secrets.POSTGRES_DB }} - EOL + echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env + echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env + echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env + echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env echo "STARTING build" From 8918ed50f01da68ab4bcadd69de1523937ccd678 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:57:14 +0530 Subject: [PATCH 25/49] Update app-deployment.yml --- .github/workflows/app-deployment.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 67c042b..957dcf0 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -27,10 +27,10 @@ jobs: echo "STARTING Deployment" sudo touch .env # Create backend .env file - echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env - echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env - echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env - echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env + sudo echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env + sudo echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env + sudo echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env + sudo echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env echo "STARTING build" From db358d3e62648f0ff5dc52136c021eb1cc49ba65 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Fri, 3 Jan 2025 18:00:45 +0530 Subject: [PATCH 26/49] Working on deployment. --- .github/workflows/app-deployment.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 957dcf0..d307135 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -27,10 +27,10 @@ jobs: echo "STARTING Deployment" sudo touch .env # Create backend .env file - sudo echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" > .env - sudo echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env - sudo echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env - sudo echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env + echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" | sudo tee .env + echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" | sudo tee -a .env + echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" | sudo tee -a .env + echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" | sudo tee -a .env echo "STARTING build" From 3ada068140f1479ec33c443801bdb07af13f8caa Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 18:06:48 +0530 Subject: [PATCH 27/49] Update db-operations.yml --- .github/workflows/db-operations.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index 7910678..6d35705 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -34,6 +34,4 @@ jobs: sleep 10 - npx prisma generate - npx prisma migrate deploy - node seeding/seed.js + npx prisma migrate reset From 00c636cb240db49f7404aaba89f7156e9b1a578a Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 18:09:44 +0530 Subject: [PATCH 28/49] Update db-operations.yml --- .github/workflows/db-operations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index 6d35705..a767d3b 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -34,4 +34,4 @@ jobs: sleep 10 - npx prisma migrate reset + npx prisma migrate reset --force From dc111df9b537a7e531a2e4a9c27c19f25b7094b6 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Fri, 3 Jan 2025 18:20:26 +0530 Subject: [PATCH 29/49] Update package.json --- deeplink-generator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeplink-generator/package.json b/deeplink-generator/package.json index 070231e..3a016cd 100644 --- a/deeplink-generator/package.json +++ b/deeplink-generator/package.json @@ -38,7 +38,7 @@ "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "15.0.3", - "prisma": "^6.1.0", + "prisma": "^6.0.1", "ts-node": "^10.9.2", "typescript": "^5.7.2" } From d6e5692ee784b55696be248203ac0639e1e71b9e Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Sun, 5 Jan 2025 17:57:58 +0530 Subject: [PATCH 30/49] Working on Deployment migration runner. --- .github/workflows/db-operations.yml | 20 ++++++-------------- deeplink-generator/docker-compose.dev.yml | 8 ++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index 7910678..d52d53b 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -15,17 +15,6 @@ jobs: port: 22 script: | cd deeplink-resolver-server/deeplink-generator - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" - source ~/.bashrc - - nvm use --lts - node -v - npm -v - - echo "STARTING Dep Installation" - sudo npm i --force echo "DB Teardown and Restart" sudo docker compose down ondc_deep_link_db @@ -33,7 +22,10 @@ jobs: sudo docker compose up -d ondc_deep_link_db sleep 10 + sudo docker run --rm \ + --network ondc_deep_link \ + -v $(pwd)/prisma:/app/prisma \ + -v $(pwd)/seeding:/app/seeding \ + node:23-alpine \ + sh -c "cd /app && npm install && npx prisma generate && npx prisma migrate deploy && node seeding/seed.js" - npx prisma generate - npx prisma migrate deploy - node seeding/seed.js diff --git a/deeplink-generator/docker-compose.dev.yml b/deeplink-generator/docker-compose.dev.yml index beafea5..c4d2826 100644 --- a/deeplink-generator/docker-compose.dev.yml +++ b/deeplink-generator/docker-compose.dev.yml @@ -7,6 +7,8 @@ services: - ./.env depends_on: - ondc_deep_link_db + networks: + - ondc_deep_link ondc_deep_link_db: image: postgres @@ -23,3 +25,9 @@ services: - "5432:5432" env_file: - ./.env + networks: + - ondc_deep_link + +networks: + ondc_deep_link: + driver: bridge \ No newline at end of file From 556fa2443ef1216a78db95a2279c085ac468be3e Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Sun, 5 Jan 2025 18:07:27 +0530 Subject: [PATCH 31/49] Fixing docker network. --- .github/workflows/db-operations.yml | 9 ++++----- deeplink-generator/Dockerfile.migration | 10 ++++++++++ deeplink-generator/docker-compose.dev.yml | 10 +--------- 3 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 deeplink-generator/Dockerfile.migration diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index d52d53b..555f59c 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -22,10 +22,9 @@ jobs: sudo docker compose up -d ondc_deep_link_db sleep 10 + + sudo docker build -t migrations-runner -f Dockerfile.migrations . sudo docker run --rm \ - --network ondc_deep_link \ - -v $(pwd)/prisma:/app/prisma \ - -v $(pwd)/seeding:/app/seeding \ - node:23-alpine \ - sh -c "cd /app && npm install && npx prisma generate && npx prisma migrate deploy && node seeding/seed.js" + --network deeplink-generator_default \ + migrations-runner diff --git a/deeplink-generator/Dockerfile.migration b/deeplink-generator/Dockerfile.migration new file mode 100644 index 0000000..bc99cfe --- /dev/null +++ b/deeplink-generator/Dockerfile.migration @@ -0,0 +1,10 @@ +FROM node:22-alpine + +WORKDIR /app + +COPY package*.json ./ +COPY . . + +RUN npm install + +CMD ["sh", "-c", "npx prisma generate && npx prisma migrate deploy && node seeding/seed.js"] diff --git a/deeplink-generator/docker-compose.dev.yml b/deeplink-generator/docker-compose.dev.yml index c4d2826..3d01240 100644 --- a/deeplink-generator/docker-compose.dev.yml +++ b/deeplink-generator/docker-compose.dev.yml @@ -7,8 +7,6 @@ services: - ./.env depends_on: - ondc_deep_link_db - networks: - - ondc_deep_link ondc_deep_link_db: image: postgres @@ -24,10 +22,4 @@ services: ports: - "5432:5432" env_file: - - ./.env - networks: - - ondc_deep_link - -networks: - ondc_deep_link: - driver: bridge \ No newline at end of file + - ./.env \ No newline at end of file From e5eaf5a0488249017d1e85bdb55eb85461a9c2f7 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Sun, 5 Jan 2025 18:09:44 +0530 Subject: [PATCH 32/49] sc --- .github/workflows/db-operations.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index 555f59c..cbe58dd 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -18,12 +18,11 @@ jobs: echo "DB Teardown and Restart" sudo docker compose down ondc_deep_link_db - sudo docker rm -f ondc_deep_link_db sudo docker compose up -d ondc_deep_link_db sleep 10 - sudo docker build -t migrations-runner -f Dockerfile.migrations . + sudo docker build -t migrations-runner -f Dockerfile.migration . sudo docker run --rm \ --network deeplink-generator_default \ migrations-runner From b04da0b45de347a8a9f25229feaf22a8c4514dbc Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Mon, 6 Jan 2025 12:27:39 +0530 Subject: [PATCH 33/49] Working on UI fixes. --- deeplink-generator/package-lock.json | 170 +++++++++++++++++- deeplink-generator/package.json | 1 + .../20250106065259_category_sub/migration.sql | 17 ++ .../prisma/migrations/migration_lock.toml | 2 +- deeplink-generator/prisma/schema.prisma | 17 +- deeplink-generator/seeding/seed.js | 61 ++++--- .../deep-link/filter/[categoryId]/page.tsx | 32 ++-- .../app/actions/get-usecase-subcategories.ts | 8 +- .../src/app/actions/publish-usecase.ts | 46 ++++- .../app/components/CustomOutlinedButton.tsx | 2 +- 10 files changed, 303 insertions(+), 53 deletions(-) create mode 100644 deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql diff --git a/deeplink-generator/package-lock.json b/deeplink-generator/package-lock.json index 266499b..c93c091 100644 --- a/deeplink-generator/package-lock.json +++ b/deeplink-generator/package-lock.json @@ -13,6 +13,7 @@ "@mui/base": "^5.0.0-beta.64", "@mui/icons-material": "^6.1.10", "@mui/material": "^6.1.9", + "@octokit/rest": "^21.0.2", "@prisma/client": "^6.1.0", "jspdf": "^2.5.2", "next": "15.0.3", @@ -29,7 +30,7 @@ "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "15.0.3", - "prisma": "^6.1.0", + "prisma": "^6.0.1", "ts-node": "^10.9.2", "typescript": "^5.7.2" } @@ -1303,6 +1304,148 @@ "node": ">=12.4.0" } }, + "node_modules/@octokit/auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", + "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.3.tgz", + "integrity": "sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==", + "dependencies": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.1.2", + "@octokit/request": "^9.1.4", + "@octokit/request-error": "^6.1.6", + "@octokit/types": "^13.6.2", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.2.tgz", + "integrity": "sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==", + "dependencies": { + "@octokit/types": "^13.6.2", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.2.tgz", + "integrity": "sha512-bdlj/CJVjpaz06NBpfHhp4kGJaRZfz7AzC+6EwUImRtrwIw8dIgJ63Xg0OzV9pRn3rIzrt5c2sa++BL0JJ8GLw==", + "dependencies": { + "@octokit/request": "^9.1.4", + "@octokit/types": "^13.6.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.6.tgz", + "integrity": "sha512-zcvqqf/+TicbTCa/Z+3w4eBJcAxCFymtc0UAIsR3dEVoNilWld4oXdscQ3laXamTszUZdusw97K8+DrbFiOwjw==", + "dependencies": { + "@octokit/types": "^13.6.2" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.2.6", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.6.tgz", + "integrity": "sha512-wMsdyHMjSfKjGINkdGKki06VEkgdEldIGstIEyGX0wbYHGByOwN/KiM+hAAlUwAtPkP3gvXtVQA9L3ITdV2tVw==", + "dependencies": { + "@octokit/types": "^13.6.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/request": { + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.4.tgz", + "integrity": "sha512-tMbOwGm6wDII6vygP3wUVqFTw3Aoo0FnVQyhihh8vVq12uO3P+vQZeo2CKMpWtPSogpACD0yyZAlVlQnjW71DA==", + "dependencies": { + "@octokit/endpoint": "^10.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.6.2", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.6.tgz", + "integrity": "sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==", + "dependencies": { + "@octokit/types": "^13.6.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.0.2.tgz", + "integrity": "sha512-+CiLisCoyWmYicH25y1cDfCrv41kRSvTq6pPWtRroRJzhsCZWZyCqGyI8foJT5LmScADSwRAnr/xo+eewL04wQ==", + "dependencies": { + "@octokit/core": "^6.1.2", + "@octokit/plugin-paginate-rest": "^11.0.0", + "@octokit/plugin-request-log": "^5.3.1", + "@octokit/plugin-rest-endpoint-methods": "^13.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.2.tgz", + "integrity": "sha512-WpbZfZUcZU77DrSW4wbsSgTPfKcp286q3ItaIgvSbBpZJlu6mnYXAkjZz6LVZPXkEvLIM8McanyZejKTYUHipA==", + "dependencies": { + "@octokit/openapi-types": "^22.2.0" + } + }, "node_modules/@pdf-lib/standard-fonts": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", @@ -2126,6 +2269,11 @@ "node": ">= 0.6.0" } }, + "node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3266,6 +3414,21 @@ "node": ">=0.10.0" } }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6119,6 +6282,11 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, + "node_modules/universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/deeplink-generator/package.json b/deeplink-generator/package.json index 3a016cd..c5c7860 100644 --- a/deeplink-generator/package.json +++ b/deeplink-generator/package.json @@ -22,6 +22,7 @@ "@mui/base": "^5.0.0-beta.64", "@mui/icons-material": "^6.1.10", "@mui/material": "^6.1.9", + "@octokit/rest": "^21.0.2", "@prisma/client": "^6.1.0", "jspdf": "^2.5.2", "next": "15.0.3", diff --git a/deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql b/deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql new file mode 100644 index 0000000..c7e0ca0 --- /dev/null +++ b/deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql @@ -0,0 +1,17 @@ +/* + Warnings: + + - Added the required column `usecaseCategoryId` to the `UsecaseSubcategory` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); + +-- AlterTable +ALTER TABLE "Usecase" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); + +-- AlterTable +ALTER TABLE "UsecaseSubcategory" ADD COLUMN "usecaseCategoryId" TEXT NOT NULL; + +-- AddForeignKey +ALTER TABLE "UsecaseSubcategory" ADD CONSTRAINT "UsecaseSubcategory_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/deeplink-generator/prisma/migrations/migration_lock.toml b/deeplink-generator/prisma/migrations/migration_lock.toml index fbffa92..648c57f 100644 --- a/deeplink-generator/prisma/migrations/migration_lock.toml +++ b/deeplink-generator/prisma/migrations/migration_lock.toml @@ -1,3 +1,3 @@ # Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) +# It should be added in your version-control system (e.g., Git) provider = "postgresql" \ No newline at end of file diff --git a/deeplink-generator/prisma/schema.prisma b/deeplink-generator/prisma/schema.prisma index 0aea5e4..3228c54 100644 --- a/deeplink-generator/prisma/schema.prisma +++ b/deeplink-generator/prisma/schema.prisma @@ -5,7 +5,7 @@ // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" binaryTargets = ["native", "linux-musl", "windows"] } @@ -61,13 +61,16 @@ model Usecase { } model UsecaseCategory { - id String @id @default(uuid()) - name String @unique - Template Template[] + id String @id @default(uuid()) + name String @unique + Template Template[] + UsecaseSubcategory UsecaseSubcategory[] } model UsecaseSubcategory { - id String @id @default(uuid()) - name String @unique - Template Template[] + id String @id @default(uuid()) + name String @unique + category UsecaseCategory @relation(fields: [usecaseCategoryId], references: [id]) + Template Template[] + usecaseCategoryId String } diff --git a/deeplink-generator/seeding/seed.js b/deeplink-generator/seeding/seed.js index 4fb414a..f62a946 100644 --- a/deeplink-generator/seeding/seed.js +++ b/deeplink-generator/seeding/seed.js @@ -7,6 +7,23 @@ const prisma = new PrismaClient(); const CATEGORIES = [ { name: "Retail", + subcategories: [ + { + name: "Search By City", + }, + { + name: "Search By Item", + }, + { + name: "Search By Category", + }, + { + name: "Multiple Seller Search", + }, + { + name: "Seller-Catalog Search", + }, + ], }, { name: "Logistics", @@ -22,24 +39,6 @@ const CATEGORIES = [ }, ]; -const SUB_CATEGORIES = [ - { - name: "Search By City", - }, - { - name: "Search By Item", - }, - { - name: "Search By Category", - }, - { - name: "Multiple Seller Search", - }, - { - name: "Seller-Catalog Search", - }, -]; - async function readJsonFiles() { const currentDir = path.join(process.cwd(), "seeding"); console.log("CWD", currentDir); @@ -57,21 +56,31 @@ async function readJsonFiles() { } async function main() { - const c = await prisma.usecaseCategory.createMany({ - data: CATEGORIES, - }); - const sc =await prisma.usecaseSubcategory.createMany({ - data: SUB_CATEGORIES, + CATEGORIES.forEach(async (category) => { + const c = await prisma.usecaseCategory.create({ + data: { + name: category.name, + }, + }); + console.log("Seeded Categories ", c); + + if (category.subcategories) { + const sc = await prisma.usecaseSubcategory.createMany({ + data: category.subcategories.map((subcategory) => ({ + name: subcategory.name, + usecaseCategoryId: c.id, + })), + }); + console.log("Seeded Sub Categories ", sc.count); + } }); - console.log("Seeded Categories ", c); - console.log("Seeded Sub Categories ", sc); const seededCategories = await prisma.usecaseCategory.findMany(); const seededSubCategories = await prisma.usecaseSubcategory.findMany(); const filesData = await readJsonFiles(); // console.log(filesData) const processedData = filesData.map((file) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const {category, subCategory, ...remainingFile} = file + const { category, subCategory, ...remainingFile } = file; return { ...remainingFile, usecaseCategoryId: seededCategories.filter( diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/filter/[categoryId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/filter/[categoryId]/page.tsx index 9fb4479..d7403e7 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/filter/[categoryId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/filter/[categoryId]/page.tsx @@ -1,7 +1,7 @@ import { getUsecaseCategories, getUsecaseSubcategories } from "@/app/actions"; import { CustomHeading, CustomOutlinedButton } from "@/app/components"; // import { formatToNormalCasing } from "@/app/utils"; -import { Grid2 as Grid, Paper } from "@mui/material"; +import { Grid2 as Grid, Paper, Typography } from "@mui/material"; import { UsecaseCategory } from "@prisma/client"; import React from "react"; @@ -9,7 +9,7 @@ export const revalidate = 3600; export const dynamicParams = true; // or false, to 404 on unknown paths export async function generateStaticParams() { - const posts: UsecaseCategory[] = await getUsecaseCategories() + const posts: UsecaseCategory[] = await getUsecaseCategories(); return posts.map((post) => ({ id: String(post.id), })); @@ -21,7 +21,7 @@ const SelectUsecaseSubcategory = async ({ params: Promise<{ categoryId: string }>; }) => { const categoryId = (await params).categoryId; - const subcategories = await getUsecaseSubcategories(); + const subcategories = await getUsecaseSubcategories(categoryId); return ( <> @@ -38,17 +38,21 @@ const SelectUsecaseSubcategory = async ({ width: "100%", }} > - - {subcategories.map((subcategory, index) => ( - - - - ))} - + {subcategories.length === 0 ? ( + No subcategories found for this category + ) : ( + + {subcategories.map((subcategory, index) => ( + + + + ))} + + )} ); diff --git a/deeplink-generator/src/app/actions/get-usecase-subcategories.ts b/deeplink-generator/src/app/actions/get-usecase-subcategories.ts index 718e535..3838ad5 100644 --- a/deeplink-generator/src/app/actions/get-usecase-subcategories.ts +++ b/deeplink-generator/src/app/actions/get-usecase-subcategories.ts @@ -1,6 +1,10 @@ "use server"; import { db } from "../../../db"; -export async function getUsecaseSubcategories() { - return db.usecaseSubcategory.findMany(); +export async function getUsecaseSubcategories(usecaseCategoryId: string) { + return db.usecaseSubcategory.findMany({ + where: { + usecaseCategoryId, + } + }); } diff --git a/deeplink-generator/src/app/actions/publish-usecase.ts b/deeplink-generator/src/app/actions/publish-usecase.ts index f387c1c..744430b 100644 --- a/deeplink-generator/src/app/actions/publish-usecase.ts +++ b/deeplink-generator/src/app/actions/publish-usecase.ts @@ -2,6 +2,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Usecase, UsecaseStage } from "@prisma/client"; import { db } from "../../../db"; +import { Octokit } from "@octokit/rest"; +const FOLDER_PATH = "usecases"; export async function publishUsecase(usecase: Usecase, form: FormData) { const updatedUsecase = await db.usecase.update({ @@ -13,5 +15,47 @@ export async function publishUsecase(usecase: Usecase, form: FormData) { name: form.get("name") as string, }, }); + if ( + (form.get("submissionOption") as UsecaseStage) === UsecaseStage.PUBLISHED + ) { + const fileName = `${updatedUsecase.id}.json`; + const filePath = `${FOLDER_PATH}/${fileName}`; + const content = Buffer.from( + JSON.stringify(updatedUsecase, null, 2) + ).toString("base64"); + + const octokit = new Octokit({ + auth: process.env.GITHUB_PAT, + }); + + try { + await octokit.repos.getContent({ + owner: process.env.GITHUB_OWNER || "", + repo: process.env.GITHUB_REPO || "", + path: FOLDER_PATH, + }); + } catch (error: any) { + if (error.status === 404) { + // Create the folder by creating a dummy file and then deleting it + await octokit.repos.createOrUpdateFileContents({ + owner: process.env.GITHUB_OWNER || "", + repo: process.env.GITHUB_REPO || "", + path: `${FOLDER_PATH}/.gitkeep`, + message: "Create hello folder", + content: Buffer.from("").toString("base64"), + }); + } + } + + // Create the new JSON file + await octokit.repos.createOrUpdateFileContents({ + owner: process.env.GITHUB_OWNER || "", + repo: process.env.GITHUB_REPO || "", + path: filePath, + message: `Add user submission ${updatedUsecase.id}`, + content, + }); + } + return updatedUsecase; -} +} \ No newline at end of file diff --git a/deeplink-generator/src/app/components/CustomOutlinedButton.tsx b/deeplink-generator/src/app/components/CustomOutlinedButton.tsx index 371494e..24db3dd 100644 --- a/deeplink-generator/src/app/components/CustomOutlinedButton.tsx +++ b/deeplink-generator/src/app/components/CustomOutlinedButton.tsx @@ -23,7 +23,7 @@ export const CustomOutlinedButton = ({ bgcolor: "transparent", width: "100%", "&:hover": { - borderWidth: 3.5, + transform: 'scale(1.04)', bgcolor: colors.grey[100], borderColor: "primary.dark", color: colors.grey[900], From c8e7e560d92e18fe7587a988035774eab1d7a61e Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Mon, 6 Jan 2025 14:28:50 +0530 Subject: [PATCH 34/49] Working on QR Download and schema changes. --- deeplink-generator/next.config.ts | 9 +- deeplink-generator/package-lock.json | 215 ++++++++++++++++-- deeplink-generator/package.json | 2 + .../20250106084158_dev/migration.sql | 5 + deeplink-generator/seeding/seed.js | 73 ++---- .../usecases/thank-you/[deepLinkId]/page.tsx | 49 +++- .../src/app/actions/publish-usecase.ts | 33 ++- .../{ => api}/resolver/[deepLinkId]/route.ts | 0 .../{ => api}/usecase/[usecaseId]/route.ts | 0 .../app/api/usecase/pdf/[usecaseId]/route.ts | 63 +++++ .../src/app/components/DownloadQr.tsx | 38 +++- .../src/app/components/SocialsList.tsx | 24 +- 12 files changed, 414 insertions(+), 97 deletions(-) create mode 100644 deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql rename deeplink-generator/src/app/{ => api}/resolver/[deepLinkId]/route.ts (100%) rename deeplink-generator/src/app/{ => api}/usecase/[usecaseId]/route.ts (100%) create mode 100644 deeplink-generator/src/app/api/usecase/pdf/[usecaseId]/route.ts diff --git a/deeplink-generator/next.config.ts b/deeplink-generator/next.config.ts index e23d70d..e636b99 100644 --- a/deeplink-generator/next.config.ts +++ b/deeplink-generator/next.config.ts @@ -1,8 +1,13 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ - output: "standalone" + /* config options here */ + output: "standalone", + images: { + remotePatterns: [ + { protocol: "https", hostname: "raw.githubusercontent.com" }, + ], + }, }; export default nextConfig; diff --git a/deeplink-generator/package-lock.json b/deeplink-generator/package-lock.json index c93c091..f0db430 100644 --- a/deeplink-generator/package-lock.json +++ b/deeplink-generator/package-lock.json @@ -18,6 +18,7 @@ "jspdf": "^2.5.2", "next": "15.0.3", "pdf-lib": "^1.17.1", + "qrcode": "^1.5.4", "react": "19.0.0", "react-dom": "19.0.0", "react-json-view-lite": "^2.0.1", @@ -26,6 +27,7 @@ }, "devDependencies": { "@types/node": "^20.17.9", + "@types/qrcode": "^1.5.5", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8", @@ -1613,6 +1615,15 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, + "node_modules/@types/qrcode": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz", + "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/raf": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", @@ -1977,7 +1988,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "devOptional": true, "engines": { "node": ">=8" } @@ -1986,7 +1996,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -2346,6 +2355,14 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001684", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", @@ -2437,6 +2454,16 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2462,7 +2489,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2473,8 +2499,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-string": { "version": "1.9.1", @@ -2647,6 +2672,14 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decompress-response": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", @@ -2731,6 +2764,11 @@ "node": ">=0.3.1" } }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3664,6 +3702,14 @@ "node": ">=10" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -4143,7 +4189,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "optional": true, "engines": { "node": ">=8" } @@ -5030,6 +5075,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -5067,7 +5120,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } @@ -5164,6 +5216,14 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -5248,6 +5308,22 @@ "node": ">=6" } }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/qrcode-generator": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz", @@ -5432,6 +5508,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -5599,8 +5688,7 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "optional": true + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "node_modules/set-function-length": { "version": "1.2.2", @@ -5805,7 +5893,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "optional": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5818,8 +5905,7 @@ "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "optional": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/string.prototype.includes": { "version": "2.0.1", @@ -5924,7 +6010,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "devOptional": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -6418,6 +6503,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, "node_modules/which-typed-array": { "version": "1.1.16", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", @@ -6455,12 +6545,30 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "devOptional": true }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -6475,6 +6583,87 @@ "node": ">= 6" } }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/deeplink-generator/package.json b/deeplink-generator/package.json index c5c7860..06d3c72 100644 --- a/deeplink-generator/package.json +++ b/deeplink-generator/package.json @@ -27,6 +27,7 @@ "jspdf": "^2.5.2", "next": "15.0.3", "pdf-lib": "^1.17.1", + "qrcode": "^1.5.4", "react": "19.0.0", "react-dom": "19.0.0", "react-json-view-lite": "^2.0.1", @@ -35,6 +36,7 @@ }, "devDependencies": { "@types/node": "^20.17.9", + "@types/qrcode": "^1.5.5", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8", diff --git a/deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql b/deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql new file mode 100644 index 0000000..d92f613 --- /dev/null +++ b/deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); + +-- AlterTable +ALTER TABLE "Usecase" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); diff --git a/deeplink-generator/seeding/seed.js b/deeplink-generator/seeding/seed.js index f62a946..c6e8991 100644 --- a/deeplink-generator/seeding/seed.js +++ b/deeplink-generator/seeding/seed.js @@ -4,41 +4,6 @@ import path from "path"; const prisma = new PrismaClient(); -const CATEGORIES = [ - { - name: "Retail", - subcategories: [ - { - name: "Search By City", - }, - { - name: "Search By Item", - }, - { - name: "Search By Category", - }, - { - name: "Multiple Seller Search", - }, - { - name: "Seller-Catalog Search", - }, - ], - }, - { - name: "Logistics", - }, - { - name: "Services", - }, - { - name: "Subscription", - }, - { - name: "IGM", - }, -]; - async function readJsonFiles() { const currentDir = path.join(process.cwd(), "seeding"); console.log("CWD", currentDir); @@ -56,28 +21,36 @@ async function readJsonFiles() { } async function main() { - CATEGORIES.forEach(async (category) => { + const filesData = await readJsonFiles(); + // console.log(filesData) + const categorySubcategoryMap = {}; + filesData.forEach(async (file) => { + const { category, subCategory } = file; + if(categorySubcategoryMap[category]) { + categorySubcategoryMap[category].push(subCategory); + } else { + categorySubcategoryMap[category] = [subCategory]; + } + }); + console.log(categorySubcategoryMap); + Object.keys(categorySubcategoryMap).forEach(async (category) => { const c = await prisma.usecaseCategory.create({ data: { - name: category.name, + name: category, }, }); - console.log("Seeded Categories ", c); - - if (category.subcategories) { - const sc = await prisma.usecaseSubcategory.createMany({ - data: category.subcategories.map((subcategory) => ({ - name: subcategory.name, - usecaseCategoryId: c.id, - })), - }); - console.log("Seeded Sub Categories ", sc.count); - } + const sc = await prisma.usecaseSubcategory.createMany({ + data: categorySubcategoryMap[category].map((subCategory) => ({ + name: subCategory, + usecaseCategoryId: c.id, + })) + }) + console.log("Seeded Subcategory", sc); }); + const seededCategories = await prisma.usecaseCategory.findMany(); const seededSubCategories = await prisma.usecaseSubcategory.findMany(); - const filesData = await readJsonFiles(); - // console.log(filesData) + const processedData = filesData.map((file) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { category, subCategory, ...remainingFile } = file; diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/thank-you/[deepLinkId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/thank-you/[deepLinkId]/page.tsx index 731be92..da10247 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/thank-you/[deepLinkId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/thank-you/[deepLinkId]/page.tsx @@ -7,7 +7,10 @@ import { import { Paper, Typography, Grid2 as Grid, Box } from "@mui/material"; import Link from "next/link"; import React from "react"; -import Divider from '@mui/material/Divider'; +import Divider from "@mui/material/Divider"; +import Image from "next/image"; +import { getUsecaseById } from "@/app/actions"; +import { redirect } from "next/navigation"; const DeepLinkThankYouPage = async ({ params, @@ -15,6 +18,10 @@ const DeepLinkThankYouPage = async ({ params: Promise<{ deepLinkId: string }>; }) => { const deepLinkId = (await params).deepLinkId; + const usecase = await getUsecaseById(deepLinkId); + if (!usecase) { + redirect("/"); + } return ( - + Thank You for using ONDC, here's your deep link! + + QR + + - + Back To Home - + diff --git a/deeplink-generator/src/app/actions/publish-usecase.ts b/deeplink-generator/src/app/actions/publish-usecase.ts index 744430b..ea16297 100644 --- a/deeplink-generator/src/app/actions/publish-usecase.ts +++ b/deeplink-generator/src/app/actions/publish-usecase.ts @@ -3,6 +3,8 @@ import { Usecase, UsecaseStage } from "@prisma/client"; import { db } from "../../../db"; import { Octokit } from "@octokit/rest"; +import QRCode from "qrcode"; + const FOLDER_PATH = "usecases"; export async function publishUsecase(usecase: Usecase, form: FormData) { @@ -15,19 +17,19 @@ export async function publishUsecase(usecase: Usecase, form: FormData) { name: form.get("name") as string, }, }); + const octokit = new Octokit({ + auth: process.env.GITHUB_PAT, + }); + if ( (form.get("submissionOption") as UsecaseStage) === UsecaseStage.PUBLISHED ) { const fileName = `${updatedUsecase.id}.json`; - const filePath = `${FOLDER_PATH}/${fileName}`; + const filePath = `${FOLDER_PATH}/json/${fileName}`; const content = Buffer.from( JSON.stringify(updatedUsecase, null, 2) ).toString("base64"); - const octokit = new Octokit({ - auth: process.env.GITHUB_PAT, - }); - try { await octokit.repos.getContent({ owner: process.env.GITHUB_OWNER || "", @@ -57,5 +59,24 @@ export async function publishUsecase(usecase: Usecase, form: FormData) { }); } + const fileName = `${updatedUsecase.id}.png`; + const filePath = `${FOLDER_PATH}/qr/${fileName}`; + + const qrCodeBase64 = await QRCode.toDataURL( + JSON.stringify(updatedUsecase.value, null, 2) + ); + + // Convert base64 to buffer (remove data:image/png;base64, prefix) + const qrCodeBuffer = Buffer.from(qrCodeBase64.split(",")[1], "base64"); + + + await octokit.repos.createOrUpdateFileContents({ + owner: process.env.GITHUB_OWNER || "", + repo: process.env.GITHUB_REPO || "", + path: filePath, + message: `Update QR code for ${updatedUsecase.id}`, + content: qrCodeBuffer.toString('base64') + }); + return updatedUsecase; -} \ No newline at end of file +} diff --git a/deeplink-generator/src/app/resolver/[deepLinkId]/route.ts b/deeplink-generator/src/app/api/resolver/[deepLinkId]/route.ts similarity index 100% rename from deeplink-generator/src/app/resolver/[deepLinkId]/route.ts rename to deeplink-generator/src/app/api/resolver/[deepLinkId]/route.ts diff --git a/deeplink-generator/src/app/usecase/[usecaseId]/route.ts b/deeplink-generator/src/app/api/usecase/[usecaseId]/route.ts similarity index 100% rename from deeplink-generator/src/app/usecase/[usecaseId]/route.ts rename to deeplink-generator/src/app/api/usecase/[usecaseId]/route.ts diff --git a/deeplink-generator/src/app/api/usecase/pdf/[usecaseId]/route.ts b/deeplink-generator/src/app/api/usecase/pdf/[usecaseId]/route.ts new file mode 100644 index 0000000..8adc374 --- /dev/null +++ b/deeplink-generator/src/app/api/usecase/pdf/[usecaseId]/route.ts @@ -0,0 +1,63 @@ +import { PDFDocument } from "pdf-lib"; +import QRCode from "qrcode"; +import fs from "fs"; +import path from "path"; +import { NextRequest, NextResponse } from "next/server"; +import { getUsecaseById } from "@/app/actions"; + +export async function GET( + req: NextRequest, + { params }: { params: Promise<{ usecaseId: string }> } +) { + try { + const usecaseId = (await params).usecaseId; + const usecase = await getUsecaseById(usecaseId); + if (!usecase) + return NextResponse.json({ error: "Usecase not found" }, { status: 404 }); + const { value } = usecase; + const qrCodeBase64 = await QRCode.toDataURL(JSON.stringify(value, null, 2)); + + // Remove data:image/png;base64 prefix + const qrCodeImageBytes = Buffer.from(qrCodeBase64.split(",")[1], "base64"); + + // Load template PDF + const templatePath = path.join(process.cwd(), "public", "template.pdf"); + const templateBytes = fs.readFileSync(templatePath); + + // Load the template PDF + const pdfDoc = await PDFDocument.load(templateBytes); + + // Embed the QR code image + const qrCodeImage = await pdfDoc.embedPng(qrCodeImageBytes); + + // Get the first page + const page = pdfDoc.getPages()[0]; + + // Calculate center position + const { width, height } = page.getSize(); + const qrSize = 200; + const x = (width - qrSize) / 2; + const y = (height - qrSize) / 2; + + // Draw QR code + page.drawImage(qrCodeImage, { + x, + y, + width: qrSize, + height: qrSize, + }); + + // Save the PDF + const pdfBytes = await pdfDoc.save(); + + return new NextResponse(pdfBytes, { + headers: { + "Content-Type": "application/pdf", + "Content-Disposition": "attachment; filename=qrcode-document.pdf", + }, + }); + } catch (error) { + console.error("Error:", error); + return NextResponse.json({ error: "Error generating PDF" }, { status: 500 }); + } +} diff --git a/deeplink-generator/src/app/components/DownloadQr.tsx b/deeplink-generator/src/app/components/DownloadQr.tsx index 42febca..0d49fea 100644 --- a/deeplink-generator/src/app/components/DownloadQr.tsx +++ b/deeplink-generator/src/app/components/DownloadQr.tsx @@ -1,15 +1,35 @@ "use client"; import { DownloadTwoTone } from "@mui/icons-material"; import { Box, IconButton, Typography } from "@mui/material"; -import React, { useState } from "react"; -import QrDialog from "./QrDialog"; +import React from "react"; type DownloadQrProps = { - link?: string; + usecaseId: string; + name: string }; -export const DownloadQr = ({ link }: DownloadQrProps) => { - const [openQrDialog, setOpenQrDialog] = useState(false); +export const DownloadQr = ({ usecaseId, name}: DownloadQrProps) => { + const handleDownloadPDF = async () => { + try { + const response = await fetch(`/api/usecase/pdf/${usecaseId}`); + if (response.ok) { + const blob = await response.blob(); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${name}.pdf`; + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + a.remove(); + } else { + alert('Error generating PDF'); + } + } catch (error) { + console.error('Error:', error); + alert('Error generating PDF'); + } + }; return ( { bgcolor: "primary.light", "&:hover": { bgcolor: "primary.dark" }, }} - onClick={() => setOpenQrDialog(true)} + onClick={handleDownloadPDF} > - setOpenQrDialog(false)} - providerName="Deep Link QR" - /> ); }; diff --git a/deeplink-generator/src/app/components/SocialsList.tsx b/deeplink-generator/src/app/components/SocialsList.tsx index 8daa922..bd68915 100644 --- a/deeplink-generator/src/app/components/SocialsList.tsx +++ b/deeplink-generator/src/app/components/SocialsList.tsx @@ -1,4 +1,4 @@ -import { Box, Grid, Typography, IconButton } from "@mui/material"; +import { Box, Grid2 as Grid, Typography, IconButton } from "@mui/material"; import React from "react"; import EmailIcon from "@mui/icons-material/Email"; import WhatsAppIcon from "@mui/icons-material/WhatsApp"; @@ -18,36 +18,42 @@ export const SocialsList = () => { flexDirection: "column", }} > - + Share directly on Socials - + - + - + - + - + - + @@ -55,4 +61,4 @@ export const SocialsList = () => { ); -}; \ No newline at end of file +}; From d93f7d25433c45bb60d6df527b89ce5d129eb24d Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Mon, 6 Jan 2025 15:23:49 +0530 Subject: [PATCH 35/49] Working on decoupling action from formData. --- .../usecases/create/[templateId]/page.tsx | 4 ++- .../usecases/publish/[deepLinkId]/page.tsx | 33 ++++++++++--------- .../usecases/thank-you/[deepLinkId]/page.tsx | 2 +- .../src/app/actions/create-deep-link.ts | 25 +++++++------- .../src/app/actions/publish-usecase.ts | 28 +++++++++++----- .../app/api/usecase/pdf/[usecaseId]/route.ts | 15 +++++---- .../src/app/utils/formDataToEntry.ts | 25 ++++++++++++++ deeplink-generator/src/app/utils/index.ts | 3 +- 8 files changed, 87 insertions(+), 48 deletions(-) create mode 100644 deeplink-generator/src/app/utils/formDataToEntry.ts diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx index bcc87c9..7392a0f 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx @@ -14,6 +14,7 @@ import Form from "next/form"; import { FillerTypeObject, flattenTemplate, + formDataToFormItemArray, inputTypeMapper, NamedEnum, } from "@/app/utils"; @@ -39,7 +40,8 @@ const GenerateDeepLinkPage = async ({ throw new Error("Form data is required"); } console.log("CREATING DEEP LINK"); - const deepLink = await createDeepLink(templateId, form); + const value = formDataToFormItemArray(form) + const deepLink = await createDeepLink({templateId, value}); console.log("Redirecting"); redirect(`/deep-link/usecases/publish/${deepLink.id}`); }; diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx index 5c5ca95..90cebd7 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx @@ -1,10 +1,15 @@ -import { getUsecaseById, publishUsecase } from "@/app/actions"; +import { + getUsecaseById, + publishUsecase, + PublishUsecaseFormType, +} from "@/app/actions"; import { CustomContainedButtom, CustomHeading, FieldName, UsecaseEditor, } from "@/app/components"; +import { formDataToEntry } from "@/app/utils"; import { Box, Divider, @@ -14,8 +19,7 @@ import { Select, TextField, Typography, - Stack - + Stack, } from "@mui/material"; import { UsecaseStage } from "@prisma/client"; import Form from "next/form"; @@ -29,12 +33,10 @@ const PublishDeepLinkPage = async ({ }) => { const deepLinkId = (await params).deepLinkId; const usecase = await getUsecaseById(deepLinkId); - const handleFormSubmit = async (form: FormData) => { + const handleFormSubmit = async (formData: FormData) => { "use server"; - if (!form) { - throw new Error("Form data is required"); - } - const publishedUsecase = await publishUsecase(usecase!, form); + const form = formDataToEntry(formData); + const publishedUsecase = await publishUsecase({ usecase: usecase!, form }); redirect(`/deep-link/usecases/thank-you/${publishedUsecase.id}`); }; return ( @@ -64,9 +66,9 @@ const PublishDeepLinkPage = async ({ alignItems="center" justifyContent="flex-start" > - +     - + - +     - + - +     - + @@ -115,11 +117,10 @@ const PublishDeepLinkPage = async ({ - + {/* {JSON.stringify(usecase?.value)} */} - - + diff --git a/deeplink-generator/src/app/actions/create-deep-link.ts b/deeplink-generator/src/app/actions/create-deep-link.ts index 2e760a6..9a92a8a 100644 --- a/deeplink-generator/src/app/actions/create-deep-link.ts +++ b/deeplink-generator/src/app/actions/create-deep-link.ts @@ -4,20 +4,17 @@ import { UsecaseStage } from "@prisma/client"; import { db } from "../../../db"; import { FormItem, inflateDeepLink } from "../utils"; -export async function createDeepLink(templateId: string, form: FormData) { - if (!form) { - throw new Error("Form data is required"); - } - console.log("Generating Deep Link...", form); - const inflatedValue = inflateDeepLink( - Array.from(form.entries()).map( - ([key, value]) => - ({ - name: key, - value: value, - } as FormItem) - ) - ); +export type CreateDeepLinkType = { + templateId: string; + value: FormItem[]; +}; + +export async function createDeepLink({ + templateId, + value, +}: CreateDeepLinkType) { + console.log("Generating Deep Link...", templateId, value); + const inflatedValue = inflateDeepLink(value); console.log("VALUE INFLATED", inflatedValue); const deepLink = await db.usecase.create({ data: { diff --git a/deeplink-generator/src/app/actions/publish-usecase.ts b/deeplink-generator/src/app/actions/publish-usecase.ts index ea16297..b95465e 100644 --- a/deeplink-generator/src/app/actions/publish-usecase.ts +++ b/deeplink-generator/src/app/actions/publish-usecase.ts @@ -5,25 +5,36 @@ import { db } from "../../../db"; import { Octokit } from "@octokit/rest"; import QRCode from "qrcode"; +export type PublishUsecaseType = { + usecase: Usecase; + form: PublishUsecaseFormType; +}; +export type PublishUsecaseFormType = { + submissionOption: UsecaseStage; + name: string; + creatorName: string; + description: string; +}; + const FOLDER_PATH = "usecases"; -export async function publishUsecase(usecase: Usecase, form: FormData) { +export async function publishUsecase({ usecase, form }: PublishUsecaseType) { const updatedUsecase = await db.usecase.update({ where: { id: usecase.id, }, data: { - usecaseStage: form.get("submissionOption") as UsecaseStage, - name: form.get("name") as string, + usecaseStage: form.submissionOption, + name: form.name, + creatorName: form.creatorName, + description: form.description, }, }); const octokit = new Octokit({ auth: process.env.GITHUB_PAT, }); - if ( - (form.get("submissionOption") as UsecaseStage) === UsecaseStage.PUBLISHED - ) { + if (form.submissionOption === UsecaseStage.PUBLISHED) { const fileName = `${updatedUsecase.id}.json`; const filePath = `${FOLDER_PATH}/json/${fileName}`; const content = Buffer.from( @@ -61,7 +72,7 @@ export async function publishUsecase(usecase: Usecase, form: FormData) { const fileName = `${updatedUsecase.id}.png`; const filePath = `${FOLDER_PATH}/qr/${fileName}`; - + const qrCodeBase64 = await QRCode.toDataURL( JSON.stringify(updatedUsecase.value, null, 2) ); @@ -69,13 +80,12 @@ export async function publishUsecase(usecase: Usecase, form: FormData) { // Convert base64 to buffer (remove data:image/png;base64, prefix) const qrCodeBuffer = Buffer.from(qrCodeBase64.split(",")[1], "base64"); - await octokit.repos.createOrUpdateFileContents({ owner: process.env.GITHUB_OWNER || "", repo: process.env.GITHUB_REPO || "", path: filePath, message: `Update QR code for ${updatedUsecase.id}`, - content: qrCodeBuffer.toString('base64') + content: qrCodeBuffer.toString("base64"), }); return updatedUsecase; diff --git a/deeplink-generator/src/app/api/usecase/pdf/[usecaseId]/route.ts b/deeplink-generator/src/app/api/usecase/pdf/[usecaseId]/route.ts index 8adc374..877103a 100644 --- a/deeplink-generator/src/app/api/usecase/pdf/[usecaseId]/route.ts +++ b/deeplink-generator/src/app/api/usecase/pdf/[usecaseId]/route.ts @@ -21,7 +21,7 @@ export async function GET( const qrCodeImageBytes = Buffer.from(qrCodeBase64.split(",")[1], "base64"); // Load template PDF - const templatePath = path.join(process.cwd(), "public", "template.pdf"); + const templatePath = path.join(process.cwd(), "public", "Poster.pdf"); const templateBytes = fs.readFileSync(templatePath); // Load the template PDF @@ -35,9 +35,9 @@ export async function GET( // Calculate center position const { width, height } = page.getSize(); - const qrSize = 200; - const x = (width - qrSize) / 2; - const y = (height - qrSize) / 2; + const qrSize = 150; + const x = (width - qrSize) / 2 + 20; + const y = (height - qrSize) / 2 + 140; // Draw QR code page.drawImage(qrCodeImage, { @@ -53,11 +53,14 @@ export async function GET( return new NextResponse(pdfBytes, { headers: { "Content-Type": "application/pdf", - "Content-Disposition": "attachment; filename=qrcode-document.pdf", + "Content-Disposition": `attachment; filename=${usecase.name || "Deep Link"}.pdf`, }, }); } catch (error) { console.error("Error:", error); - return NextResponse.json({ error: "Error generating PDF" }, { status: 500 }); + return NextResponse.json( + { error: "Error generating PDF" }, + { status: 500 } + ); } } diff --git a/deeplink-generator/src/app/utils/formDataToEntry.ts b/deeplink-generator/src/app/utils/formDataToEntry.ts new file mode 100644 index 0000000..964afbb --- /dev/null +++ b/deeplink-generator/src/app/utils/formDataToEntry.ts @@ -0,0 +1,25 @@ +import { FormItem } from "./inflate"; + +export function formDataToEntry(form: FormData): T { + const entries = Array.from(form.entries()); + const result = entries.reduce((acc, [key, value]) => { + return { + ...acc, + [key]: value, + }; + }, {} as T); + + return result; +} + +export function formDataToFormItemArray(form: FormData): FormItem[] { + const result = Array.from(form.entries()).map( + ([key, value]) => + ({ + name: key, + value: value, + } as FormItem) + ); + + return result; +} diff --git a/deeplink-generator/src/app/utils/index.ts b/deeplink-generator/src/app/utils/index.ts index a933079..fc6e3a3 100644 --- a/deeplink-generator/src/app/utils/index.ts +++ b/deeplink-generator/src/app/utils/index.ts @@ -2,4 +2,5 @@ export * from "./flatten"; export * from "./inflate"; export * from "./formatToNormalCasing"; export * from "./inputTypeMapper"; -export * from "./getTextPlacement" \ No newline at end of file +export * from "./getTextPlacement" +export * from "./formDataToEntry" \ No newline at end of file From 69acf93280418a677a8ebfa8591a2d2fa5eeac59 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Mon, 6 Jan 2025 15:51:56 +0530 Subject: [PATCH 36/49] Minor changes. --- .../src/app/actions/publish-usecase.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/deeplink-generator/src/app/actions/publish-usecase.ts b/deeplink-generator/src/app/actions/publish-usecase.ts index b95465e..f6d7eac 100644 --- a/deeplink-generator/src/app/actions/publish-usecase.ts +++ b/deeplink-generator/src/app/actions/publish-usecase.ts @@ -31,7 +31,7 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { }, }); const octokit = new Octokit({ - auth: process.env.GITHUB_PAT, + auth: process.env.GIT_PAT, }); if (form.submissionOption === UsecaseStage.PUBLISHED) { @@ -43,16 +43,16 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { try { await octokit.repos.getContent({ - owner: process.env.GITHUB_OWNER || "", - repo: process.env.GITHUB_REPO || "", + owner: process.env.GIT_OWNER || "", + repo: process.env.GIT_REPO || "", path: FOLDER_PATH, }); } catch (error: any) { if (error.status === 404) { // Create the folder by creating a dummy file and then deleting it await octokit.repos.createOrUpdateFileContents({ - owner: process.env.GITHUB_OWNER || "", - repo: process.env.GITHUB_REPO || "", + owner: process.env.GIT_OWNER || "", + repo: process.env.GIT_REPO || "", path: `${FOLDER_PATH}/.gitkeep`, message: "Create hello folder", content: Buffer.from("").toString("base64"), @@ -62,8 +62,8 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { // Create the new JSON file await octokit.repos.createOrUpdateFileContents({ - owner: process.env.GITHUB_OWNER || "", - repo: process.env.GITHUB_REPO || "", + owner: process.env.GIT_OWNER || "", + repo: process.env.GIT_REPO || "", path: filePath, message: `Add user submission ${updatedUsecase.id}`, content, @@ -81,8 +81,8 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { const qrCodeBuffer = Buffer.from(qrCodeBase64.split(",")[1], "base64"); await octokit.repos.createOrUpdateFileContents({ - owner: process.env.GITHUB_OWNER || "", - repo: process.env.GITHUB_REPO || "", + owner: process.env.GIT_OWNER || "", + repo: process.env.GIT_REPO || "", path: filePath, message: `Update QR code for ${updatedUsecase.id}`, content: qrCodeBuffer.toString("base64"), From 4ec9f57b9f3f137a5bab4687b2930738b468f685 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Tue, 7 Jan 2025 10:27:57 +0530 Subject: [PATCH 37/49] Logging the migrations. --- deeplink-generator/seeding/seed.js | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/deeplink-generator/seeding/seed.js b/deeplink-generator/seeding/seed.js index c6e8991..e9303ad 100644 --- a/deeplink-generator/seeding/seed.js +++ b/deeplink-generator/seeding/seed.js @@ -26,7 +26,7 @@ async function main() { const categorySubcategoryMap = {}; filesData.forEach(async (file) => { const { category, subCategory } = file; - if(categorySubcategoryMap[category]) { + if (categorySubcategoryMap[category]) { categorySubcategoryMap[category].push(subCategory); } else { categorySubcategoryMap[category] = [subCategory]; @@ -43,17 +43,38 @@ async function main() { data: categorySubcategoryMap[category].map((subCategory) => ({ name: subCategory, usecaseCategoryId: c.id, - })) - }) + })), + }); console.log("Seeded Subcategory", sc); }); const seededCategories = await prisma.usecaseCategory.findMany(); const seededSubCategories = await prisma.usecaseSubcategory.findMany(); + console.log("Seeded Categories", seededCategories); + console.log("Seeded SubCategories", seededSubCategories); + const processedData = filesData.map((file) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { category, subCategory, ...remainingFile } = file; + console.log( + "FILTERED category", + file.category.toLowerCase(), + seededCategories.filter( + (category) => + category.name.toLowerCase() === file.category.toLowerCase() + )[0].id + ); + + console.log( + "FILTERED sub-category", + file.subCategory.toLowerCase(), + seededCategories.filter( + (category) => + category.name.toLowerCase() === file.category.toLowerCase() + )[0].id + ); + return { ...remainingFile, usecaseCategoryId: seededCategories.filter( From 1c73edc2da20c017ff3ee04ee18a2a22827ad579 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Tue, 7 Jan 2025 11:31:18 +0530 Subject: [PATCH 38/49] Debugging. --- deeplink-generator/src/app/actions/publish-usecase.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/deeplink-generator/src/app/actions/publish-usecase.ts b/deeplink-generator/src/app/actions/publish-usecase.ts index f6d7eac..395fc76 100644 --- a/deeplink-generator/src/app/actions/publish-usecase.ts +++ b/deeplink-generator/src/app/actions/publish-usecase.ts @@ -30,6 +30,7 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { description: form.description, }, }); + console.log("ENVs", process.env.GIT_OWNER, process.env.GIT_REPO, process.env.GIT_PAT); const octokit = new Octokit({ auth: process.env.GIT_PAT, }); From f4ac96a8404905217256c469ef2b1d71d72bd5a7 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Tue, 7 Jan 2025 11:50:23 +0530 Subject: [PATCH 39/49] changing env name. --- .../src/app/actions/publish-usecase.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/deeplink-generator/src/app/actions/publish-usecase.ts b/deeplink-generator/src/app/actions/publish-usecase.ts index 395fc76..b9eafc6 100644 --- a/deeplink-generator/src/app/actions/publish-usecase.ts +++ b/deeplink-generator/src/app/actions/publish-usecase.ts @@ -30,9 +30,9 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { description: form.description, }, }); - console.log("ENVs", process.env.GIT_OWNER, process.env.GIT_REPO, process.env.GIT_PAT); + console.log("ENVs", process.env.OWNER_NAME_REPO, process.env.STORAGE_REPO_NAME, process.env.ACCESS_TOKEN_REPO); const octokit = new Octokit({ - auth: process.env.GIT_PAT, + auth: process.env.ACCESS_TOKEN_REPO, }); if (form.submissionOption === UsecaseStage.PUBLISHED) { @@ -44,16 +44,16 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { try { await octokit.repos.getContent({ - owner: process.env.GIT_OWNER || "", - repo: process.env.GIT_REPO || "", + owner: process.env.OWNER_NAME_REPO || "", + repo: process.env.STORAGE_REPO_NAME || "", path: FOLDER_PATH, }); } catch (error: any) { if (error.status === 404) { // Create the folder by creating a dummy file and then deleting it await octokit.repos.createOrUpdateFileContents({ - owner: process.env.GIT_OWNER || "", - repo: process.env.GIT_REPO || "", + owner: process.env.OWNER_NAME_REPO || "", + repo: process.env.STORAGE_REPO_NAME || "", path: `${FOLDER_PATH}/.gitkeep`, message: "Create hello folder", content: Buffer.from("").toString("base64"), @@ -63,8 +63,8 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { // Create the new JSON file await octokit.repos.createOrUpdateFileContents({ - owner: process.env.GIT_OWNER || "", - repo: process.env.GIT_REPO || "", + owner: process.env.OWNER_NAME_REPO || "", + repo: process.env.STORAGE_REPO_NAME || "", path: filePath, message: `Add user submission ${updatedUsecase.id}`, content, @@ -82,8 +82,8 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { const qrCodeBuffer = Buffer.from(qrCodeBase64.split(",")[1], "base64"); await octokit.repos.createOrUpdateFileContents({ - owner: process.env.GIT_OWNER || "", - repo: process.env.GIT_REPO || "", + owner: process.env.OWNER_NAME_REPO || "", + repo: process.env.STORAGE_REPO_NAME || "", path: filePath, message: `Update QR code for ${updatedUsecase.id}`, content: qrCodeBuffer.toString("base64"), From 4465473142e50f5bd97ea2cb5ac9528975629792 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:58:59 +0530 Subject: [PATCH 40/49] Update app-deployment.yml --- .github/workflows/app-deployment.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index d307135..9d44420 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -31,8 +31,10 @@ jobs: echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" | sudo tee -a .env echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" | sudo tee -a .env echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" | sudo tee -a .env - - + echo "ACCESS_TOKEN_REPO=${{ secrets.ACCESS_TOKEN_REPO }}" | sudo tee -a .env + echo "OWNER_NAME_REPO=${{ secrets.OWNER_NAME_REPO }}" | sudo tee -a .env + echo "STORAGE_REPO_NAME=${{ secrets.STORAGE_REPO_NAME }}" | sudo tee -a .env + echo "STARTING build" sudo cp docker-compose.dev.yml docker-compose.yml sudo docker compose up ondc_deep_link_app -d --build From e7bb242edfec252a2c866d4c4501c2ba0fe6a813 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Wed, 8 Jan 2025 17:05:33 +0530 Subject: [PATCH 41/49] Working on UI changes. --- deeplink-generator/README.md | 61 ++++++++++++------- deeplink-generator/docker-compose.local.yml | 17 +----- deeplink-generator/example.env | 9 +++ deeplink-generator/nginx/nginx.conf | 44 ------------- .../usecases/create/[templateId]/page.tsx | 36 ++++++++--- .../usecases/publish/[deepLinkId]/page.tsx | 5 +- ...{create-deep-link.ts => create-usecase.ts} | 9 +-- deeplink-generator/src/app/actions/index.ts | 2 +- .../src/app/actions/publish-usecase.ts | 22 +++++-- .../DeepLinkBackgroundContainer.tsx | 41 +++++++++---- .../src/app/components/UsecaseEditor.tsx | 19 +++--- .../src/app/providers/AppThemeProvider.tsx | 25 ++++++-- 12 files changed, 159 insertions(+), 131 deletions(-) create mode 100644 deeplink-generator/example.env delete mode 100644 deeplink-generator/nginx/nginx.conf rename deeplink-generator/src/app/actions/{create-deep-link.ts => create-usecase.ts} (63%) diff --git a/deeplink-generator/README.md b/deeplink-generator/README.md index e215bc4..1a104c5 100644 --- a/deeplink-generator/README.md +++ b/deeplink-generator/README.md @@ -1,36 +1,51 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Deep Link v2 +This repo holds the code for Deep Link v2 App. It is a utility provided to generate usecases from predefined templates. There are 3 personas concerned in this app. These personas are: +1. Admin +2. User +3. Consumer -## Getting Started +Out of these, currently, the User flow has been enabled. Remaining flows are under development and will be introduced in future releases. -First, run the development server: +## Admin +The admin is responsible to **creating** templates. These templates inherit schema from the Base Beckn template. The admin can then add fields to the template. There are 3 types of fields: +1. Pre-filled - These fields are defined and their values are provided by the admin. These fields are not editable by the user. So any usecase created with these templates will have the same values for these fields. +2. User-filled - The admin can mark a field as "user-filled". This field will be editable by the user. The user can provide their own values for these fields during usecase generation. +3. PG - These fields are filled during consumption phase. The usecase created with the template will have these fields marked and the resolver server will communicate with the agent to fill these fields. -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` +## User +The user is responsible to **creating** usecases. The user can select a template and then add fields to the template. The user can then generate the usecase. The usecase will be generated in the form of a JSON file. The user can then use this JSON file to create a usecase on the Beckn network. -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +The usecases may be kept private or public (saveed to GitHub). Additionally, when the usecase is either published privately (submitted) or published, the usecase QR is saved on Github. This QR contains the ID of the usecase. The resolver server can then use this ID to fetch the usecase. -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +## Consumer +The consumer is responsible to **consuming** usecases. The consumer can scan the QR of the usecase and the resolver server will fetch the usecase. The resolver server will then communicate with the "consuming" agent to fill the fields marked as PG. The resolver server will then return the usecase to the consumer. The consumer can then use this usecase to create a usecase on the Beckn network. -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +Currently, hitting `/api/resolver/{usecase_id}` will resolve the usecase. *Note*: While this returns the usecase, it does not resolve the usecase. The usecase is resolved when the resolver server communicates with the agent and all the post-generation fields are filled. **Post Generation fields are denoted with `{{}}`. -## Learn More +## Developer Guide -To learn more about Next.js, take a look at the following resources: +This section is meant for developers who want to contribute to the project. The project is built using NextJS and PostgreSQL. Prisma ORM has been used. -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +### Prerequisites +Knowledge of NextJS 15, TypeScript, PostgreSQL, Docker, Docker Compose, Prisma ORM, and Beckn Protocol is required. -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +You need to have the following before you can start developing: +1. NodeJS (^22.0) +2. Docker & Docker Compose -## Deploy on Vercel +### Steps to start the project +1. Clone the repo +2. RUN `cd deeplink-generator` +3. Run `npm install` +4. Copy the `example.env` file to `.env` and update the values as needed. +5. Copy the `docker-compose.local.yml` to `docker-compose.yml`. +6. RUN `docker compose up ondc_deep_link_db -d`. This will start the database in a docker container. +7. RUN `npx prisma migrate dev` followed by `npx prisma seed` to seed the database. +8. RUN `npm run dev` to start the project. -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +### Seeding the database +The database comes pre-seeded with the templates defined inside the `seeding` directory once step 7 in the previous sub-section is completed. The seeding script automatically picks up on the usecase category and sub-category as defined in the templates and seeds them accordingly. -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +## Contributors: +1. [Abhik Banerjee](https://github.com/abhik-wil) +2. [Sonali Shakya](https://github.com/sonalishakya) \ No newline at end of file diff --git a/deeplink-generator/docker-compose.local.yml b/deeplink-generator/docker-compose.local.yml index c50e928..89acf76 100644 --- a/deeplink-generator/docker-compose.local.yml +++ b/deeplink-generator/docker-compose.local.yml @@ -5,7 +5,6 @@ services: - "8080:3000" environment: - DATABASE_URL=${DATABASE_URL} - - ENV=${ENV} depends_on: - ondc_deep_link_db @@ -23,17 +22,7 @@ services: ports: - "5432:5432" environment: - - POSTGRES_USER=${DB_USER} - - POSTGRES_PASSWORD=${DB_PASS} - - POSTGRES_DB=${DB_NAME} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} - ondc_deep_link_nginx: - image: nginx:alpine - ports: - - "80:80" - - "443:443" - volumes: - - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - - ./ssl:/etc/nginx/ssl:ro - depends_on: - - ondc_deep_link_app diff --git a/deeplink-generator/example.env b/deeplink-generator/example.env new file mode 100644 index 0000000..bd72af4 --- /dev/null +++ b/deeplink-generator/example.env @@ -0,0 +1,9 @@ +POSTGRES_USER= +POSTGRES_PASS= +POSTGRES_NAME= +DATABASE_URL= + +ACCESS_TOKEN_REPO= +OWNER_NAME_REPO= +STORAGE_REPO_NAME= + diff --git a/deeplink-generator/nginx/nginx.conf b/deeplink-generator/nginx/nginx.conf deleted file mode 100644 index 41f2441..0000000 --- a/deeplink-generator/nginx/nginx.conf +++ /dev/null @@ -1,44 +0,0 @@ -# events { -# worker_connections 1024; -# } - -http { - upstream nextjs_upstream { - server localhost:8080; - } - - server { - listen 80; - listen [::]:80; - server_name deeplink.resolver.ondc.org; - - return 301 https://$server_name$request_uri; - } - - server { - listen 443 ssl; - listen [::]:443 ssl; - server_name _; - - ssl_certificate /etc/letsencrypt/live/deeplink.resolver.ondc.org/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/deeplink.resolver.ondc.org/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!aNULL:!MD5; - - location / { - proxy_pass http://nextjs_upstream; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - } -} - -add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; -add_header X-Frame-Options DENY; -add_header X-Content-Type-Options nosniff; diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx index 7392a0f..b2fe72b 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx @@ -18,7 +18,7 @@ import { inputTypeMapper, NamedEnum, } from "@/app/utils"; -import { createDeepLink, getTemplateById } from "@/app/actions"; +import { createUsecase, getTemplateById } from "@/app/actions"; import { CustomContainedButtom, CustomHeading, @@ -33,17 +33,33 @@ const GenerateDeepLinkPage = async ({ const templateId = (await params).templateId; const template = await getTemplateById(templateId); const templateValue = flattenTemplate(template!.value); - console.log("TEMPLATE VALUE", templateValue); + const handleSubmit = async (form: FormData) => { "use server"; - if (!form) { - throw new Error("Form data is required"); + const value = formDataToFormItemArray(form); + let valid = true; + value.forEach((item) => { + if (item.value.startsWith("{{") || item.value.endsWith("}}")) { + valid = false; + throw alert(`Invalid Input`); + } + }); + + if (valid) { + const deepLink = await createUsecase({ + templateId, + value: value.map(({ name, value }) => { + try { + JSON.parse(value); + return { name, value: `{{${name}}}` }; + } catch { + return { name, value }; + } + }), + }); + + redirect(`/deep-link/usecases/publish/${deepLink.id}`); } - console.log("CREATING DEEP LINK"); - const value = formDataToFormItemArray(form) - const deepLink = await createDeepLink({templateId, value}); - console.log("Redirecting"); - redirect(`/deep-link/usecases/publish/${deepLink.id}`); }; return ( <> @@ -158,6 +174,7 @@ const GenerateDeepLinkPage = async ({ } fullWidth name={key} + required > {(templateValue[key] as FillerTypeObject).enum?.map( (value, index) => ( @@ -189,6 +206,7 @@ const GenerateDeepLinkPage = async ({ sx={{ ml: 1 }} name={key} fullWidth + required /> )} diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx index 90cebd7..69a4eae 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx @@ -79,7 +79,7 @@ const PublishDeepLinkPage = async ({ >     - +     - + @@ -106,6 +106,7 @@ const PublishDeepLinkPage = async ({ name="submissionOption" defaultValue={UsecaseStage.PUBLISHED} fullWidth + required > Save Private diff --git a/deeplink-generator/src/app/actions/create-deep-link.ts b/deeplink-generator/src/app/actions/create-usecase.ts similarity index 63% rename from deeplink-generator/src/app/actions/create-deep-link.ts rename to deeplink-generator/src/app/actions/create-usecase.ts index 9a92a8a..14bbfd5 100644 --- a/deeplink-generator/src/app/actions/create-deep-link.ts +++ b/deeplink-generator/src/app/actions/create-usecase.ts @@ -4,18 +4,16 @@ import { UsecaseStage } from "@prisma/client"; import { db } from "../../../db"; import { FormItem, inflateDeepLink } from "../utils"; -export type CreateDeepLinkType = { +export type CreateUsecaseType = { templateId: string; value: FormItem[]; }; -export async function createDeepLink({ +export async function createUsecase({ templateId, value, -}: CreateDeepLinkType) { - console.log("Generating Deep Link...", templateId, value); +}: CreateUsecaseType) { const inflatedValue = inflateDeepLink(value); - console.log("VALUE INFLATED", inflatedValue); const deepLink = await db.usecase.create({ data: { templateId, @@ -23,7 +21,6 @@ export async function createDeepLink({ usecaseStage: UsecaseStage.DRAFT, }, }); - console.log("USE CASE CREATED", deepLink); return deepLink; } diff --git a/deeplink-generator/src/app/actions/index.ts b/deeplink-generator/src/app/actions/index.ts index 8f89ffe..62d0e07 100644 --- a/deeplink-generator/src/app/actions/index.ts +++ b/deeplink-generator/src/app/actions/index.ts @@ -1,4 +1,4 @@ -export * from "./create-deep-link"; +export * from "./create-usecase"; export * from "./create-template"; export * from "./publish-template"; export * from "./get-usecase-categories"; diff --git a/deeplink-generator/src/app/actions/publish-usecase.ts b/deeplink-generator/src/app/actions/publish-usecase.ts index b9eafc6..ec95bef 100644 --- a/deeplink-generator/src/app/actions/publish-usecase.ts +++ b/deeplink-generator/src/app/actions/publish-usecase.ts @@ -19,7 +19,7 @@ export type PublishUsecaseFormType = { const FOLDER_PATH = "usecases"; export async function publishUsecase({ usecase, form }: PublishUsecaseType) { - const updatedUsecase = await db.usecase.update({ + const { value, ...updatedUsecase } = await db.usecase.update({ where: { id: usecase.id, }, @@ -30,7 +30,6 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { description: form.description, }, }); - console.log("ENVs", process.env.OWNER_NAME_REPO, process.env.STORAGE_REPO_NAME, process.env.ACCESS_TOKEN_REPO); const octokit = new Octokit({ auth: process.env.ACCESS_TOKEN_REPO, }); @@ -38,7 +37,13 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { if (form.submissionOption === UsecaseStage.PUBLISHED) { const fileName = `${updatedUsecase.id}.json`; const filePath = `${FOLDER_PATH}/json/${fileName}`; - const content = Buffer.from( + const content = Buffer.from(JSON.stringify(value, null, 2)).toString( + "base64" + ); + + const metaFileName = `${updatedUsecase.id}-meta.json`; + const metaFilePath = `${FOLDER_PATH}/meta/${metaFileName}`; + const metaContent = Buffer.from( JSON.stringify(updatedUsecase, null, 2) ).toString("base64"); @@ -61,7 +66,6 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { } } - // Create the new JSON file await octokit.repos.createOrUpdateFileContents({ owner: process.env.OWNER_NAME_REPO || "", repo: process.env.STORAGE_REPO_NAME || "", @@ -69,13 +73,21 @@ export async function publishUsecase({ usecase, form }: PublishUsecaseType) { message: `Add user submission ${updatedUsecase.id}`, content, }); + + await octokit.repos.createOrUpdateFileContents({ + owner: process.env.OWNER_NAME_REPO || "", + repo: process.env.STORAGE_REPO_NAME || "", + path: metaFilePath, + message: `Add user submission ${updatedUsecase.id}-meta`, + content: metaContent, + }); } const fileName = `${updatedUsecase.id}.png`; const filePath = `${FOLDER_PATH}/qr/${fileName}`; const qrCodeBase64 = await QRCode.toDataURL( - JSON.stringify(updatedUsecase.value, null, 2) + `beckn://github.ondc.ret10/${updatedUsecase.id}` ); // Convert base64 to buffer (remove data:image/png;base64, prefix) diff --git a/deeplink-generator/src/app/components/DeepLinkBackgroundContainer.tsx b/deeplink-generator/src/app/components/DeepLinkBackgroundContainer.tsx index 42035fa..b8ab4f3 100644 --- a/deeplink-generator/src/app/components/DeepLinkBackgroundContainer.tsx +++ b/deeplink-generator/src/app/components/DeepLinkBackgroundContainer.tsx @@ -1,20 +1,37 @@ -import { Container } from "@mui/material"; +"use client"; +import { Container, Box } from "@mui/material"; import React from "react"; +import { styled } from "@mui/material/styles"; +import { useAppTheme } from "../hooks"; export const DeepLinkBackgroundContainer: React.FC<{ children: React.ReactNode; }> = ({ children }) => { + const { mode } = useAppTheme(); + const Background = styled(Box)(() => ({ + backgroundImage: `url(${ + mode === "light" ? "/background.jpg" : "/background_inverted.jpg" + })`, + backgroundSize: "cover", + backgroundPosition: "center", + minHeight: "100vh", + display: "flex", + justifyContent: "center", + alignItems: "center", + })); return ( - - {children} - + + + {children} + + ); }; diff --git a/deeplink-generator/src/app/components/UsecaseEditor.tsx b/deeplink-generator/src/app/components/UsecaseEditor.tsx index e64062f..91ca839 100644 --- a/deeplink-generator/src/app/components/UsecaseEditor.tsx +++ b/deeplink-generator/src/app/components/UsecaseEditor.tsx @@ -26,14 +26,6 @@ type UsecaseEditorProps = { export const UsecaseEditor = ({ usecase }: UsecaseEditorProps) => { const flattenedTemplate = flattenTemplate(usecase.template.value); const flattenedUsecase = flattenTemplate(usecase.value); - console.log("usecase", flattenedUsecase); - console.log("flattened template", flattenedTemplate); - console.log( - "Included", - Object.keys(flattenedUsecase).every((t) => - Object.keys(flattenedTemplate).includes(t) - ) - ); const [usecaseState, setUsecaseState] = useState(flattenedUsecase); const [editUsecase, setEditUsecase] = useState(false); const handleUsecaseEdit = async () => { @@ -101,10 +93,17 @@ export const UsecaseEditor = ({ usecase }: UsecaseEditorProps) => { "admin" && (flattenedTemplate[key] as FillerTypeObject).filler !== "pg" ) + .filter( + (key) => + !( + (flattenedUsecase[key] as string).startsWith("{{") && + (flattenedUsecase[key] as string).endsWith("}}") + ) + ) .map((key: string) => ( - - + + {typeof flattenedTemplate[key] === "string" ? ( diff --git a/deeplink-generator/src/app/providers/AppThemeProvider.tsx b/deeplink-generator/src/app/providers/AppThemeProvider.tsx index 08b563f..45a3edc 100644 --- a/deeplink-generator/src/app/providers/AppThemeProvider.tsx +++ b/deeplink-generator/src/app/providers/AppThemeProvider.tsx @@ -1,15 +1,13 @@ "use client"; import { CssBaseline, ThemeProvider } from "@mui/material"; -import React, { createContext, useMemo, useState } from "react"; +import React, { createContext, useMemo, useState, useEffect } from "react"; import { darkTheme, lightTheme } from "../assets/themes"; type AppThemeContextType = { toggleTheme: () => void; mode: "light" | "dark"; }; -export const AppThemeContext = createContext( - {} as AppThemeContextType -); +export const AppThemeContext = createContext({} as AppThemeContextType); export const AppThemeProvider = ({ children, @@ -17,10 +15,22 @@ export const AppThemeProvider = ({ children: React.ReactNode; }) => { const [mode, setMode] = useState<"light" | "dark">("light"); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + const savedMode = localStorage.getItem('theme-mode') as "light" | "dark"; + if (savedMode) { + setMode(savedMode); + } + setMounted(true); + }, []); const toggleTheme = () => { - setMode((prevMode) => (prevMode === "light" ? "dark" : "light")); + const newMode = mode === "light" ? "dark" : "light"; + setMode(newMode); + localStorage.setItem('theme-mode', newMode); }; + const themeContextValue = useMemo( () => ({ toggleTheme, @@ -28,6 +38,11 @@ export const AppThemeProvider = ({ }), [mode] ); + + if (!mounted) { + return <>{children}; + } + return ( From 3aab7ac4ce9f315d48a747d3aa2ee67e3d8035a2 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:15:26 +0530 Subject: [PATCH 42/49] Dev (#13) * Working on Github Actions. * Working on Deployment. * Fix: Github Actions. * Fixed username. * Working on deployment. * Update clone-and-setup.yml * Update clone-and-setup.yml * Update clone-and-setup.yml * sanity commit. * snaity commit. * s. * sc. * logging. * Update app-deployment.yml * Update app-deployment.yml * Update db-operations.yml * Update db-operations.yml * Update db-operations.yml * Update db-operations.yml * Update db-operations.yml * Removing unused classes. * Update db-operations.yml * Update db-operations.yml * Update app-deployment.yml * Update app-deployment.yml * Working on deployment. * Update db-operations.yml * Update db-operations.yml * Update package.json * Working on Deployment migration runner. * Fixing docker network. * sc * Working on UI fixes. * Working on QR Download and schema changes. * Working on decoupling action from formData. * Minor changes. * Logging the migrations. * Debugging. * changing env name. * Update app-deployment.yml * Working on UI changes. * Resolved bug; consolidated migrations. --- deeplink-generator/README.md | 61 ++++++++------ deeplink-generator/docker-compose.local.yml | 17 +--- deeplink-generator/example.env | 9 ++ deeplink-generator/nginx/nginx.conf | 44 ---------- .../20241213065301_init/migration.sql | 42 ---------- .../20241216062431_making/migration.sql | 49 ----------- .../20241216063510_ynuque/migration.sql | 12 --- .../20241218052010_qr/migration.sql | 2 - .../20241224045445_tuncate_uuid/migration.sql | 19 ----- .../20241224045549_desc/migration.sql | 7 -- .../20241227074217_xyz/migration.sql | 5 -- .../20250106065259_category_sub/migration.sql | 17 ---- .../20250106084158_dev/migration.sql | 5 -- .../migration.sql | 77 ++++++++++++++++++ deeplink-generator/seeding/seed.js | 40 +++++---- .../usecases/create/[templateId]/page.tsx | 38 +++++++-- .../usecases/publish/[deepLinkId]/page.tsx | 10 +-- ...{create-deep-link.ts => create-usecase.ts} | 9 +- deeplink-generator/src/app/actions/index.ts | 2 +- .../src/app/actions/publish-usecase.ts | 22 +++-- .../DeepLinkBackgroundContainer.tsx | 41 +++++++--- .../src/app/components/UsecaseEditor.tsx | 19 ++--- deeplink-generator/src/app/favicon.ico | Bin 25931 -> 2042 bytes deeplink-generator/src/app/ondc_logo.png | Bin 0 -> 41615 bytes .../src/app/providers/AppThemeProvider.tsx | 25 ++++-- deeplink-generator/src/app/utils/inflate.ts | 1 - 26 files changed, 265 insertions(+), 308 deletions(-) create mode 100644 deeplink-generator/example.env delete mode 100644 deeplink-generator/nginx/nginx.conf delete mode 100644 deeplink-generator/prisma/migrations/20241213065301_init/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241216062431_making/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241216063510_ynuque/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241218052010_qr/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241224045445_tuncate_uuid/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241224045549_desc/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241227074217_xyz/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql create mode 100644 deeplink-generator/prisma/migrations/20250113062321_init_13_01_25/migration.sql rename deeplink-generator/src/app/actions/{create-deep-link.ts => create-usecase.ts} (63%) create mode 100644 deeplink-generator/src/app/ondc_logo.png diff --git a/deeplink-generator/README.md b/deeplink-generator/README.md index e215bc4..1a104c5 100644 --- a/deeplink-generator/README.md +++ b/deeplink-generator/README.md @@ -1,36 +1,51 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Deep Link v2 +This repo holds the code for Deep Link v2 App. It is a utility provided to generate usecases from predefined templates. There are 3 personas concerned in this app. These personas are: +1. Admin +2. User +3. Consumer -## Getting Started +Out of these, currently, the User flow has been enabled. Remaining flows are under development and will be introduced in future releases. -First, run the development server: +## Admin +The admin is responsible to **creating** templates. These templates inherit schema from the Base Beckn template. The admin can then add fields to the template. There are 3 types of fields: +1. Pre-filled - These fields are defined and their values are provided by the admin. These fields are not editable by the user. So any usecase created with these templates will have the same values for these fields. +2. User-filled - The admin can mark a field as "user-filled". This field will be editable by the user. The user can provide their own values for these fields during usecase generation. +3. PG - These fields are filled during consumption phase. The usecase created with the template will have these fields marked and the resolver server will communicate with the agent to fill these fields. -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` +## User +The user is responsible to **creating** usecases. The user can select a template and then add fields to the template. The user can then generate the usecase. The usecase will be generated in the form of a JSON file. The user can then use this JSON file to create a usecase on the Beckn network. -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +The usecases may be kept private or public (saveed to GitHub). Additionally, when the usecase is either published privately (submitted) or published, the usecase QR is saved on Github. This QR contains the ID of the usecase. The resolver server can then use this ID to fetch the usecase. -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +## Consumer +The consumer is responsible to **consuming** usecases. The consumer can scan the QR of the usecase and the resolver server will fetch the usecase. The resolver server will then communicate with the "consuming" agent to fill the fields marked as PG. The resolver server will then return the usecase to the consumer. The consumer can then use this usecase to create a usecase on the Beckn network. -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +Currently, hitting `/api/resolver/{usecase_id}` will resolve the usecase. *Note*: While this returns the usecase, it does not resolve the usecase. The usecase is resolved when the resolver server communicates with the agent and all the post-generation fields are filled. **Post Generation fields are denoted with `{{}}`. -## Learn More +## Developer Guide -To learn more about Next.js, take a look at the following resources: +This section is meant for developers who want to contribute to the project. The project is built using NextJS and PostgreSQL. Prisma ORM has been used. -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +### Prerequisites +Knowledge of NextJS 15, TypeScript, PostgreSQL, Docker, Docker Compose, Prisma ORM, and Beckn Protocol is required. -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +You need to have the following before you can start developing: +1. NodeJS (^22.0) +2. Docker & Docker Compose -## Deploy on Vercel +### Steps to start the project +1. Clone the repo +2. RUN `cd deeplink-generator` +3. Run `npm install` +4. Copy the `example.env` file to `.env` and update the values as needed. +5. Copy the `docker-compose.local.yml` to `docker-compose.yml`. +6. RUN `docker compose up ondc_deep_link_db -d`. This will start the database in a docker container. +7. RUN `npx prisma migrate dev` followed by `npx prisma seed` to seed the database. +8. RUN `npm run dev` to start the project. -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +### Seeding the database +The database comes pre-seeded with the templates defined inside the `seeding` directory once step 7 in the previous sub-section is completed. The seeding script automatically picks up on the usecase category and sub-category as defined in the templates and seeds them accordingly. -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +## Contributors: +1. [Abhik Banerjee](https://github.com/abhik-wil) +2. [Sonali Shakya](https://github.com/sonalishakya) \ No newline at end of file diff --git a/deeplink-generator/docker-compose.local.yml b/deeplink-generator/docker-compose.local.yml index c50e928..89acf76 100644 --- a/deeplink-generator/docker-compose.local.yml +++ b/deeplink-generator/docker-compose.local.yml @@ -5,7 +5,6 @@ services: - "8080:3000" environment: - DATABASE_URL=${DATABASE_URL} - - ENV=${ENV} depends_on: - ondc_deep_link_db @@ -23,17 +22,7 @@ services: ports: - "5432:5432" environment: - - POSTGRES_USER=${DB_USER} - - POSTGRES_PASSWORD=${DB_PASS} - - POSTGRES_DB=${DB_NAME} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} - ondc_deep_link_nginx: - image: nginx:alpine - ports: - - "80:80" - - "443:443" - volumes: - - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - - ./ssl:/etc/nginx/ssl:ro - depends_on: - - ondc_deep_link_app diff --git a/deeplink-generator/example.env b/deeplink-generator/example.env new file mode 100644 index 0000000..bd72af4 --- /dev/null +++ b/deeplink-generator/example.env @@ -0,0 +1,9 @@ +POSTGRES_USER= +POSTGRES_PASS= +POSTGRES_NAME= +DATABASE_URL= + +ACCESS_TOKEN_REPO= +OWNER_NAME_REPO= +STORAGE_REPO_NAME= + diff --git a/deeplink-generator/nginx/nginx.conf b/deeplink-generator/nginx/nginx.conf deleted file mode 100644 index 41f2441..0000000 --- a/deeplink-generator/nginx/nginx.conf +++ /dev/null @@ -1,44 +0,0 @@ -# events { -# worker_connections 1024; -# } - -http { - upstream nextjs_upstream { - server localhost:8080; - } - - server { - listen 80; - listen [::]:80; - server_name deeplink.resolver.ondc.org; - - return 301 https://$server_name$request_uri; - } - - server { - listen 443 ssl; - listen [::]:443 ssl; - server_name _; - - ssl_certificate /etc/letsencrypt/live/deeplink.resolver.ondc.org/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/deeplink.resolver.ondc.org/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers HIGH:!aNULL:!MD5; - - location / { - proxy_pass http://nextjs_upstream; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - } -} - -add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; -add_header X-Frame-Options DENY; -add_header X-Content-Type-Options nosniff; diff --git a/deeplink-generator/prisma/migrations/20241213065301_init/migration.sql b/deeplink-generator/prisma/migrations/20241213065301_init/migration.sql deleted file mode 100644 index 2575eed..0000000 --- a/deeplink-generator/prisma/migrations/20241213065301_init/migration.sql +++ /dev/null @@ -1,42 +0,0 @@ --- CreateEnum -CREATE TYPE "TemplateStage" AS ENUM ('DRAFT', 'REVIEW', 'SUBMITTED', 'PUBLISHED'); - --- CreateEnum -CREATE TYPE "UsecaseStage" AS ENUM ('DRAFT', 'REVIEW', 'SUBMITTED', 'PUBLISHED'); - --- CreateEnum -CREATE TYPE "UsecaseCategory" AS ENUM ('RETAIL', 'LOGISTICS', 'MOBILITY', 'SERVICES'); - --- CreateEnum -CREATE TYPE "UsecaseSubcategory" AS ENUM ('SEARCH_CATELOG', 'SEARCH_BY_CITY', 'SEARCH_BY_ITEM', 'SEARCH_BY_CATEGORY', 'OFFERS'); - --- CreateTable -CREATE TABLE "Template" ( - "id" TEXT NOT NULL, - "name" TEXT, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "value" JSONB NOT NULL, - "category" "UsecaseCategory", - "subCategory" "UsecaseSubcategory", - "templateStage" "TemplateStage" NOT NULL, - - CONSTRAINT "Template_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Usecase" ( - "id" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "value" JSONB NOT NULL, - "templateId" TEXT NOT NULL, - "name" TEXT, - "usecaseStage" "UsecaseStage" NOT NULL, - - CONSTRAINT "Usecase_pkey" PRIMARY KEY ("id") -); - --- AddForeignKey -ALTER TABLE "Usecase" ADD CONSTRAINT "Usecase_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/deeplink-generator/prisma/migrations/20241216062431_making/migration.sql b/deeplink-generator/prisma/migrations/20241216062431_making/migration.sql deleted file mode 100644 index c9db6b9..0000000 --- a/deeplink-generator/prisma/migrations/20241216062431_making/migration.sql +++ /dev/null @@ -1,49 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `category` on the `Template` table. All the data in the column will be lost. - - You are about to drop the column `subCategory` on the `Template` table. All the data in the column will be lost. - - A unique constraint covering the columns `[usecaseCategoryId,usecaseSubcategoryId]` on the table `Template` will be added. If there are existing duplicate values, this will fail. - - Added the required column `usecaseCategoryId` to the `Template` table without a default value. This is not possible if the table is not empty. - - Added the required column `usecaseSubcategoryId` to the `Template` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Template" DROP COLUMN "category", -DROP COLUMN "subCategory", -ADD COLUMN "usecaseCategoryId" TEXT NOT NULL, -ADD COLUMN "usecaseSubcategoryId" TEXT NOT NULL; - --- DropEnum -DROP TYPE "UsecaseCategory"; - --- DropEnum -DROP TYPE "UsecaseSubcategory"; - --- CreateTable -CREATE TABLE "UsecaseCategory" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - - CONSTRAINT "UsecaseCategory_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "UsecaseSubcategory" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - - CONSTRAINT "UsecaseSubcategory_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "Template_usecaseCategoryId_usecaseSubcategoryId_idx" ON "Template"("usecaseCategoryId", "usecaseSubcategoryId"); - --- CreateIndex -CREATE UNIQUE INDEX "Template_usecaseCategoryId_usecaseSubcategoryId_key" ON "Template"("usecaseCategoryId", "usecaseSubcategoryId"); - --- AddForeignKey -ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseSubcategoryId_fkey" FOREIGN KEY ("usecaseSubcategoryId") REFERENCES "UsecaseSubcategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/deeplink-generator/prisma/migrations/20241216063510_ynuque/migration.sql b/deeplink-generator/prisma/migrations/20241216063510_ynuque/migration.sql deleted file mode 100644 index aa6dec3..0000000 --- a/deeplink-generator/prisma/migrations/20241216063510_ynuque/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[name]` on the table `UsecaseCategory` will be added. If there are existing duplicate values, this will fail. - - A unique constraint covering the columns `[name]` on the table `UsecaseSubcategory` will be added. If there are existing duplicate values, this will fail. - -*/ --- CreateIndex -CREATE UNIQUE INDEX "UsecaseCategory_name_key" ON "UsecaseCategory"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "UsecaseSubcategory_name_key" ON "UsecaseSubcategory"("name"); diff --git a/deeplink-generator/prisma/migrations/20241218052010_qr/migration.sql b/deeplink-generator/prisma/migrations/20241218052010_qr/migration.sql deleted file mode 100644 index 0c63c0c..0000000 --- a/deeplink-generator/prisma/migrations/20241218052010_qr/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Usecase" ADD COLUMN "qrPdfLink" TEXT; diff --git a/deeplink-generator/prisma/migrations/20241224045445_tuncate_uuid/migration.sql b/deeplink-generator/prisma/migrations/20241224045445_tuncate_uuid/migration.sql deleted file mode 100644 index c394722..0000000 --- a/deeplink-generator/prisma/migrations/20241224045445_tuncate_uuid/migration.sql +++ /dev/null @@ -1,19 +0,0 @@ --- DropForeignKey -ALTER TABLE "Template" DROP CONSTRAINT "Template_usecaseCategoryId_fkey"; - --- DropForeignKey -ALTER TABLE "Template" DROP CONSTRAINT "Template_usecaseSubcategoryId_fkey"; - --- AlterTable -ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16), -ALTER COLUMN "usecaseCategoryId" DROP NOT NULL, -ALTER COLUMN "usecaseSubcategoryId" DROP NOT NULL; - --- AlterTable -ALTER TABLE "Usecase" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AddForeignKey -ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseSubcategoryId_fkey" FOREIGN KEY ("usecaseSubcategoryId") REFERENCES "UsecaseSubcategory"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/deeplink-generator/prisma/migrations/20241224045549_desc/migration.sql b/deeplink-generator/prisma/migrations/20241224045549_desc/migration.sql deleted file mode 100644 index c8c6476..0000000 --- a/deeplink-generator/prisma/migrations/20241224045549_desc/migration.sql +++ /dev/null @@ -1,7 +0,0 @@ --- AlterTable -ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AlterTable -ALTER TABLE "Usecase" ADD COLUMN "creatorName" TEXT, -ADD COLUMN "description" TEXT, -ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); diff --git a/deeplink-generator/prisma/migrations/20241227074217_xyz/migration.sql b/deeplink-generator/prisma/migrations/20241227074217_xyz/migration.sql deleted file mode 100644 index d92f613..0000000 --- a/deeplink-generator/prisma/migrations/20241227074217_xyz/migration.sql +++ /dev/null @@ -1,5 +0,0 @@ --- AlterTable -ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AlterTable -ALTER TABLE "Usecase" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); diff --git a/deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql b/deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql deleted file mode 100644 index c7e0ca0..0000000 --- a/deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql +++ /dev/null @@ -1,17 +0,0 @@ -/* - Warnings: - - - Added the required column `usecaseCategoryId` to the `UsecaseSubcategory` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AlterTable -ALTER TABLE "Usecase" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AlterTable -ALTER TABLE "UsecaseSubcategory" ADD COLUMN "usecaseCategoryId" TEXT NOT NULL; - --- AddForeignKey -ALTER TABLE "UsecaseSubcategory" ADD CONSTRAINT "UsecaseSubcategory_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql b/deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql deleted file mode 100644 index d92f613..0000000 --- a/deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql +++ /dev/null @@ -1,5 +0,0 @@ --- AlterTable -ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AlterTable -ALTER TABLE "Usecase" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); diff --git a/deeplink-generator/prisma/migrations/20250113062321_init_13_01_25/migration.sql b/deeplink-generator/prisma/migrations/20250113062321_init_13_01_25/migration.sql new file mode 100644 index 0000000..bc1b672 --- /dev/null +++ b/deeplink-generator/prisma/migrations/20250113062321_init_13_01_25/migration.sql @@ -0,0 +1,77 @@ +-- CreateEnum +CREATE TYPE "TemplateStage" AS ENUM ('DRAFT', 'REVIEW', 'SUBMITTED', 'PUBLISHED'); + +-- CreateEnum +CREATE TYPE "UsecaseStage" AS ENUM ('DRAFT', 'REVIEW', 'SUBMITTED', 'PUBLISHED'); + +-- CreateTable +CREATE TABLE "Template" ( + "id" TEXT NOT NULL DEFAULT substr(gen_random_uuid()::text, 1, 16), + "name" TEXT, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "value" JSONB NOT NULL, + "templateStage" "TemplateStage" NOT NULL, + "usecaseCategoryId" TEXT, + "usecaseSubcategoryId" TEXT, + + CONSTRAINT "Template_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Usecase" ( + "id" TEXT NOT NULL DEFAULT substr(gen_random_uuid()::text, 1, 16), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "value" JSONB NOT NULL, + "templateId" TEXT NOT NULL, + "name" TEXT, + "description" TEXT, + "creatorName" TEXT, + "qrPdfLink" TEXT, + "usecaseStage" "UsecaseStage" NOT NULL, + + CONSTRAINT "Usecase_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UsecaseCategory" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "UsecaseCategory_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UsecaseSubcategory" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "usecaseCategoryId" TEXT NOT NULL, + + CONSTRAINT "UsecaseSubcategory_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "Template_usecaseCategoryId_usecaseSubcategoryId_idx" ON "Template"("usecaseCategoryId", "usecaseSubcategoryId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Template_usecaseCategoryId_usecaseSubcategoryId_key" ON "Template"("usecaseCategoryId", "usecaseSubcategoryId"); + +-- CreateIndex +CREATE UNIQUE INDEX "UsecaseCategory_name_key" ON "UsecaseCategory"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "UsecaseSubcategory_name_key" ON "UsecaseSubcategory"("name"); + +-- AddForeignKey +ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseSubcategoryId_fkey" FOREIGN KEY ("usecaseSubcategoryId") REFERENCES "UsecaseSubcategory"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Usecase" ADD CONSTRAINT "Usecase_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UsecaseSubcategory" ADD CONSTRAINT "UsecaseSubcategory_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/deeplink-generator/seeding/seed.js b/deeplink-generator/seeding/seed.js index e9303ad..6d41950 100644 --- a/deeplink-generator/seeding/seed.js +++ b/deeplink-generator/seeding/seed.js @@ -33,24 +33,34 @@ async function main() { } }); console.log(categorySubcategoryMap); - Object.keys(categorySubcategoryMap).forEach(async (category) => { - const c = await prisma.usecaseCategory.create({ - data: { - name: category, - }, - }); - const sc = await prisma.usecaseSubcategory.createMany({ - data: categorySubcategoryMap[category].map((subCategory) => ({ - name: subCategory, - usecaseCategoryId: c.id, - })), - }); - console.log("Seeded Subcategory", sc); - }); + const createdCategories = await Promise.all( + Object.keys(categorySubcategoryMap).map((category) => + prisma.usecaseCategory.create({ + data: { + name: category, + UsecaseSubcategory: { + createMany: { + data: categorySubcategoryMap[category].map((name) => ({ + name, + })), + }, + }, + }, + include: { + UsecaseSubcategory: true, + } + }) + ) + ); + console.log("Seeded Categories", createdCategories); const seededCategories = await prisma.usecaseCategory.findMany(); const seededSubCategories = await prisma.usecaseSubcategory.findMany(); + console.log("Seeded Categories", seededCategories); + console.log("Seeded SubCategories", seededSubCategories); + + console.log("Seeded Categories", seededCategories); console.log("Seeded SubCategories", seededSubCategories); @@ -65,7 +75,7 @@ async function main() { category.name.toLowerCase() === file.category.toLowerCase() )[0].id ); - + console.log( "FILTERED sub-category", file.subCategory.toLowerCase(), diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx index 7392a0f..62c342b 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx @@ -18,7 +18,7 @@ import { inputTypeMapper, NamedEnum, } from "@/app/utils"; -import { createDeepLink, getTemplateById } from "@/app/actions"; +import { createUsecase, getTemplateById } from "@/app/actions"; import { CustomContainedButtom, CustomHeading, @@ -33,17 +33,35 @@ const GenerateDeepLinkPage = async ({ const templateId = (await params).templateId; const template = await getTemplateById(templateId); const templateValue = flattenTemplate(template!.value); - console.log("TEMPLATE VALUE", templateValue); const handleSubmit = async (form: FormData) => { "use server"; - if (!form) { - throw new Error("Form data is required"); + const value = formDataToFormItemArray(form); + let valid = true; + value.forEach((item) => { + if (item.value.startsWith("{{") || item.value.endsWith("}}")) { + valid = false; + throw alert(`Invalid Input`); + } + }); + + if (valid) { + const deepLink = await createUsecase({ + templateId, + value: value.map(({ name, value }) => { + try { + const v = JSON.parse(value); + if (typeof v !== "object") { + throw new Error("Primitive values as strings are not allowed"); + } + return { name, value: `{{${name}}}` }; + } catch { + return { name, value }; + } + }), + }); + + redirect(`/deep-link/usecases/publish/${deepLink.id}`); } - console.log("CREATING DEEP LINK"); - const value = formDataToFormItemArray(form) - const deepLink = await createDeepLink({templateId, value}); - console.log("Redirecting"); - redirect(`/deep-link/usecases/publish/${deepLink.id}`); }; return ( <> @@ -158,6 +176,7 @@ const GenerateDeepLinkPage = async ({ } fullWidth name={key} + required > {(templateValue[key] as FillerTypeObject).enum?.map( (value, index) => ( @@ -189,6 +208,7 @@ const GenerateDeepLinkPage = async ({ sx={{ ml: 1 }} name={key} fullWidth + required /> )} diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx index 90cebd7..916604d 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx @@ -67,7 +67,6 @@ const PublishDeepLinkPage = async ({ justifyContent="flex-start" > -     @@ -78,8 +77,7 @@ const PublishDeepLinkPage = async ({ justifyContent="flex-start" > -     - + -     - + @@ -106,6 +103,7 @@ const PublishDeepLinkPage = async ({ name="submissionOption" defaultValue={UsecaseStage.PUBLISHED} fullWidth + required > Save Private @@ -117,11 +115,13 @@ const PublishDeepLinkPage = async ({ + {/* {JSON.stringify(usecase?.value)} */} + = ({ children }) => { + const { mode } = useAppTheme(); + const Background = styled(Box)(() => ({ + backgroundImage: `url(${ + mode === "light" ? "/background.jpg" : "/background_inverted.jpg" + })`, + backgroundSize: "cover", + backgroundPosition: "center", + minHeight: "100vh", + display: "flex", + justifyContent: "center", + alignItems: "center", + })); return ( - - {children} - + + + {children} + + ); }; diff --git a/deeplink-generator/src/app/components/UsecaseEditor.tsx b/deeplink-generator/src/app/components/UsecaseEditor.tsx index e64062f..6199fb8 100644 --- a/deeplink-generator/src/app/components/UsecaseEditor.tsx +++ b/deeplink-generator/src/app/components/UsecaseEditor.tsx @@ -26,14 +26,6 @@ type UsecaseEditorProps = { export const UsecaseEditor = ({ usecase }: UsecaseEditorProps) => { const flattenedTemplate = flattenTemplate(usecase.template.value); const flattenedUsecase = flattenTemplate(usecase.value); - console.log("usecase", flattenedUsecase); - console.log("flattened template", flattenedTemplate); - console.log( - "Included", - Object.keys(flattenedUsecase).every((t) => - Object.keys(flattenedTemplate).includes(t) - ) - ); const [usecaseState, setUsecaseState] = useState(flattenedUsecase); const [editUsecase, setEditUsecase] = useState(false); const handleUsecaseEdit = async () => { @@ -101,10 +93,17 @@ export const UsecaseEditor = ({ usecase }: UsecaseEditorProps) => { "admin" && (flattenedTemplate[key] as FillerTypeObject).filler !== "pg" ) + .filter( + (key) => + !( + (flattenedUsecase[key]!.toString()).startsWith("{{") && + (flattenedUsecase[key]!.toString()).endsWith("}}") + ) + ) .map((key: string) => ( - - + + {typeof flattenedTemplate[key] === "string" ? ( diff --git a/deeplink-generator/src/app/favicon.ico b/deeplink-generator/src/app/favicon.ico index 718d6fea4835ec2d246af9800eddb7ffb276240c..53699d92548b52747342633e62699a1e84b13f65 100644 GIT binary patch literal 2042 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 LrJugUA?W`A8`#=m diff --git a/deeplink-generator/src/app/ondc_logo.png b/deeplink-generator/src/app/ondc_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..979e470a491454925e4f043851c8e5dab414c0b6 GIT binary patch literal 41615 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 literal 0 HcmV?d00001 diff --git a/deeplink-generator/src/app/providers/AppThemeProvider.tsx b/deeplink-generator/src/app/providers/AppThemeProvider.tsx index 08b563f..45a3edc 100644 --- a/deeplink-generator/src/app/providers/AppThemeProvider.tsx +++ b/deeplink-generator/src/app/providers/AppThemeProvider.tsx @@ -1,15 +1,13 @@ "use client"; import { CssBaseline, ThemeProvider } from "@mui/material"; -import React, { createContext, useMemo, useState } from "react"; +import React, { createContext, useMemo, useState, useEffect } from "react"; import { darkTheme, lightTheme } from "../assets/themes"; type AppThemeContextType = { toggleTheme: () => void; mode: "light" | "dark"; }; -export const AppThemeContext = createContext( - {} as AppThemeContextType -); +export const AppThemeContext = createContext({} as AppThemeContextType); export const AppThemeProvider = ({ children, @@ -17,10 +15,22 @@ export const AppThemeProvider = ({ children: React.ReactNode; }) => { const [mode, setMode] = useState<"light" | "dark">("light"); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + const savedMode = localStorage.getItem('theme-mode') as "light" | "dark"; + if (savedMode) { + setMode(savedMode); + } + setMounted(true); + }, []); const toggleTheme = () => { - setMode((prevMode) => (prevMode === "light" ? "dark" : "light")); + const newMode = mode === "light" ? "dark" : "light"; + setMode(newMode); + localStorage.setItem('theme-mode', newMode); }; + const themeContextValue = useMemo( () => ({ toggleTheme, @@ -28,6 +38,11 @@ export const AppThemeProvider = ({ }), [mode] ); + + if (!mounted) { + return <>{children}; + } + return ( diff --git a/deeplink-generator/src/app/utils/inflate.ts b/deeplink-generator/src/app/utils/inflate.ts index 61af218..85430b4 100644 --- a/deeplink-generator/src/app/utils/inflate.ts +++ b/deeplink-generator/src/app/utils/inflate.ts @@ -94,6 +94,5 @@ export function inflateDeepLink(formData: FormItem[]) { formData.forEach((item) => { setNestedValue(result, item.name, item.value); }); - return result; } From 8e9a5a386ca01a6c151f03a315006f170ffd87e0 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee <106070541+abhik-wil@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:17:56 +0530 Subject: [PATCH 43/49] Dev (#1) * Baseline for Master (#12) * Working on Github Actions. * Working on Deployment. * Fix: Github Actions. * Fixed username. * Working on deployment. * Update clone-and-setup.yml * Update clone-and-setup.yml * Update clone-and-setup.yml * sanity commit. * snaity commit. * s. * sc. * logging. * Update app-deployment.yml * Update app-deployment.yml * Update db-operations.yml * Update db-operations.yml * Update db-operations.yml * Update db-operations.yml * Update db-operations.yml * Removing unused classes. * Update db-operations.yml * Update db-operations.yml * Update app-deployment.yml * Update app-deployment.yml * Working on deployment. * Update db-operations.yml * Update db-operations.yml * Update package.json * Working on Deployment migration runner. * Fixing docker network. * sc * Working on UI fixes. * Working on QR Download and schema changes. * Working on decoupling action from formData. * Minor changes. * Logging the migrations. * Debugging. * changing env name. * Update app-deployment.yml * Resolved bug; consolidated migrations. --- .../20241213065301_init/migration.sql | 42 ---------- .../20241216062431_making/migration.sql | 49 ----------- .../20241216063510_ynuque/migration.sql | 12 --- .../20241218052010_qr/migration.sql | 2 - .../20241224045445_tuncate_uuid/migration.sql | 19 ----- .../20241224045549_desc/migration.sql | 7 -- .../20241227074217_xyz/migration.sql | 5 -- .../20250106065259_category_sub/migration.sql | 17 ---- .../20250106084158_dev/migration.sql | 5 -- .../migration.sql | 77 ++++++++++++++++++ deeplink-generator/seeding/seed.js | 40 +++++---- .../usecases/create/[templateId]/page.tsx | 6 +- .../usecases/publish/[deepLinkId]/page.tsx | 5 +- .../src/app/components/UsecaseEditor.tsx | 4 +- deeplink-generator/src/app/favicon.ico | Bin 25931 -> 2042 bytes deeplink-generator/src/app/ondc_logo.png | Bin 0 -> 41615 bytes deeplink-generator/src/app/utils/inflate.ts | 1 - 17 files changed, 110 insertions(+), 181 deletions(-) delete mode 100644 deeplink-generator/prisma/migrations/20241213065301_init/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241216062431_making/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241216063510_ynuque/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241218052010_qr/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241224045445_tuncate_uuid/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241224045549_desc/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20241227074217_xyz/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql delete mode 100644 deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql create mode 100644 deeplink-generator/prisma/migrations/20250113062321_init_13_01_25/migration.sql create mode 100644 deeplink-generator/src/app/ondc_logo.png diff --git a/deeplink-generator/prisma/migrations/20241213065301_init/migration.sql b/deeplink-generator/prisma/migrations/20241213065301_init/migration.sql deleted file mode 100644 index 2575eed..0000000 --- a/deeplink-generator/prisma/migrations/20241213065301_init/migration.sql +++ /dev/null @@ -1,42 +0,0 @@ --- CreateEnum -CREATE TYPE "TemplateStage" AS ENUM ('DRAFT', 'REVIEW', 'SUBMITTED', 'PUBLISHED'); - --- CreateEnum -CREATE TYPE "UsecaseStage" AS ENUM ('DRAFT', 'REVIEW', 'SUBMITTED', 'PUBLISHED'); - --- CreateEnum -CREATE TYPE "UsecaseCategory" AS ENUM ('RETAIL', 'LOGISTICS', 'MOBILITY', 'SERVICES'); - --- CreateEnum -CREATE TYPE "UsecaseSubcategory" AS ENUM ('SEARCH_CATELOG', 'SEARCH_BY_CITY', 'SEARCH_BY_ITEM', 'SEARCH_BY_CATEGORY', 'OFFERS'); - --- CreateTable -CREATE TABLE "Template" ( - "id" TEXT NOT NULL, - "name" TEXT, - "description" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "value" JSONB NOT NULL, - "category" "UsecaseCategory", - "subCategory" "UsecaseSubcategory", - "templateStage" "TemplateStage" NOT NULL, - - CONSTRAINT "Template_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Usecase" ( - "id" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "value" JSONB NOT NULL, - "templateId" TEXT NOT NULL, - "name" TEXT, - "usecaseStage" "UsecaseStage" NOT NULL, - - CONSTRAINT "Usecase_pkey" PRIMARY KEY ("id") -); - --- AddForeignKey -ALTER TABLE "Usecase" ADD CONSTRAINT "Usecase_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/deeplink-generator/prisma/migrations/20241216062431_making/migration.sql b/deeplink-generator/prisma/migrations/20241216062431_making/migration.sql deleted file mode 100644 index c9db6b9..0000000 --- a/deeplink-generator/prisma/migrations/20241216062431_making/migration.sql +++ /dev/null @@ -1,49 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `category` on the `Template` table. All the data in the column will be lost. - - You are about to drop the column `subCategory` on the `Template` table. All the data in the column will be lost. - - A unique constraint covering the columns `[usecaseCategoryId,usecaseSubcategoryId]` on the table `Template` will be added. If there are existing duplicate values, this will fail. - - Added the required column `usecaseCategoryId` to the `Template` table without a default value. This is not possible if the table is not empty. - - Added the required column `usecaseSubcategoryId` to the `Template` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Template" DROP COLUMN "category", -DROP COLUMN "subCategory", -ADD COLUMN "usecaseCategoryId" TEXT NOT NULL, -ADD COLUMN "usecaseSubcategoryId" TEXT NOT NULL; - --- DropEnum -DROP TYPE "UsecaseCategory"; - --- DropEnum -DROP TYPE "UsecaseSubcategory"; - --- CreateTable -CREATE TABLE "UsecaseCategory" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - - CONSTRAINT "UsecaseCategory_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "UsecaseSubcategory" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - - CONSTRAINT "UsecaseSubcategory_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "Template_usecaseCategoryId_usecaseSubcategoryId_idx" ON "Template"("usecaseCategoryId", "usecaseSubcategoryId"); - --- CreateIndex -CREATE UNIQUE INDEX "Template_usecaseCategoryId_usecaseSubcategoryId_key" ON "Template"("usecaseCategoryId", "usecaseSubcategoryId"); - --- AddForeignKey -ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseSubcategoryId_fkey" FOREIGN KEY ("usecaseSubcategoryId") REFERENCES "UsecaseSubcategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/deeplink-generator/prisma/migrations/20241216063510_ynuque/migration.sql b/deeplink-generator/prisma/migrations/20241216063510_ynuque/migration.sql deleted file mode 100644 index aa6dec3..0000000 --- a/deeplink-generator/prisma/migrations/20241216063510_ynuque/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[name]` on the table `UsecaseCategory` will be added. If there are existing duplicate values, this will fail. - - A unique constraint covering the columns `[name]` on the table `UsecaseSubcategory` will be added. If there are existing duplicate values, this will fail. - -*/ --- CreateIndex -CREATE UNIQUE INDEX "UsecaseCategory_name_key" ON "UsecaseCategory"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "UsecaseSubcategory_name_key" ON "UsecaseSubcategory"("name"); diff --git a/deeplink-generator/prisma/migrations/20241218052010_qr/migration.sql b/deeplink-generator/prisma/migrations/20241218052010_qr/migration.sql deleted file mode 100644 index 0c63c0c..0000000 --- a/deeplink-generator/prisma/migrations/20241218052010_qr/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE "Usecase" ADD COLUMN "qrPdfLink" TEXT; diff --git a/deeplink-generator/prisma/migrations/20241224045445_tuncate_uuid/migration.sql b/deeplink-generator/prisma/migrations/20241224045445_tuncate_uuid/migration.sql deleted file mode 100644 index c394722..0000000 --- a/deeplink-generator/prisma/migrations/20241224045445_tuncate_uuid/migration.sql +++ /dev/null @@ -1,19 +0,0 @@ --- DropForeignKey -ALTER TABLE "Template" DROP CONSTRAINT "Template_usecaseCategoryId_fkey"; - --- DropForeignKey -ALTER TABLE "Template" DROP CONSTRAINT "Template_usecaseSubcategoryId_fkey"; - --- AlterTable -ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16), -ALTER COLUMN "usecaseCategoryId" DROP NOT NULL, -ALTER COLUMN "usecaseSubcategoryId" DROP NOT NULL; - --- AlterTable -ALTER TABLE "Usecase" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AddForeignKey -ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseSubcategoryId_fkey" FOREIGN KEY ("usecaseSubcategoryId") REFERENCES "UsecaseSubcategory"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/deeplink-generator/prisma/migrations/20241224045549_desc/migration.sql b/deeplink-generator/prisma/migrations/20241224045549_desc/migration.sql deleted file mode 100644 index c8c6476..0000000 --- a/deeplink-generator/prisma/migrations/20241224045549_desc/migration.sql +++ /dev/null @@ -1,7 +0,0 @@ --- AlterTable -ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AlterTable -ALTER TABLE "Usecase" ADD COLUMN "creatorName" TEXT, -ADD COLUMN "description" TEXT, -ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); diff --git a/deeplink-generator/prisma/migrations/20241227074217_xyz/migration.sql b/deeplink-generator/prisma/migrations/20241227074217_xyz/migration.sql deleted file mode 100644 index d92f613..0000000 --- a/deeplink-generator/prisma/migrations/20241227074217_xyz/migration.sql +++ /dev/null @@ -1,5 +0,0 @@ --- AlterTable -ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AlterTable -ALTER TABLE "Usecase" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); diff --git a/deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql b/deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql deleted file mode 100644 index c7e0ca0..0000000 --- a/deeplink-generator/prisma/migrations/20250106065259_category_sub/migration.sql +++ /dev/null @@ -1,17 +0,0 @@ -/* - Warnings: - - - Added the required column `usecaseCategoryId` to the `UsecaseSubcategory` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AlterTable -ALTER TABLE "Usecase" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AlterTable -ALTER TABLE "UsecaseSubcategory" ADD COLUMN "usecaseCategoryId" TEXT NOT NULL; - --- AddForeignKey -ALTER TABLE "UsecaseSubcategory" ADD CONSTRAINT "UsecaseSubcategory_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql b/deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql deleted file mode 100644 index d92f613..0000000 --- a/deeplink-generator/prisma/migrations/20250106084158_dev/migration.sql +++ /dev/null @@ -1,5 +0,0 @@ --- AlterTable -ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); - --- AlterTable -ALTER TABLE "Usecase" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); diff --git a/deeplink-generator/prisma/migrations/20250113062321_init_13_01_25/migration.sql b/deeplink-generator/prisma/migrations/20250113062321_init_13_01_25/migration.sql new file mode 100644 index 0000000..bc1b672 --- /dev/null +++ b/deeplink-generator/prisma/migrations/20250113062321_init_13_01_25/migration.sql @@ -0,0 +1,77 @@ +-- CreateEnum +CREATE TYPE "TemplateStage" AS ENUM ('DRAFT', 'REVIEW', 'SUBMITTED', 'PUBLISHED'); + +-- CreateEnum +CREATE TYPE "UsecaseStage" AS ENUM ('DRAFT', 'REVIEW', 'SUBMITTED', 'PUBLISHED'); + +-- CreateTable +CREATE TABLE "Template" ( + "id" TEXT NOT NULL DEFAULT substr(gen_random_uuid()::text, 1, 16), + "name" TEXT, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "value" JSONB NOT NULL, + "templateStage" "TemplateStage" NOT NULL, + "usecaseCategoryId" TEXT, + "usecaseSubcategoryId" TEXT, + + CONSTRAINT "Template_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Usecase" ( + "id" TEXT NOT NULL DEFAULT substr(gen_random_uuid()::text, 1, 16), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "value" JSONB NOT NULL, + "templateId" TEXT NOT NULL, + "name" TEXT, + "description" TEXT, + "creatorName" TEXT, + "qrPdfLink" TEXT, + "usecaseStage" "UsecaseStage" NOT NULL, + + CONSTRAINT "Usecase_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UsecaseCategory" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "UsecaseCategory_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "UsecaseSubcategory" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "usecaseCategoryId" TEXT NOT NULL, + + CONSTRAINT "UsecaseSubcategory_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "Template_usecaseCategoryId_usecaseSubcategoryId_idx" ON "Template"("usecaseCategoryId", "usecaseSubcategoryId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Template_usecaseCategoryId_usecaseSubcategoryId_key" ON "Template"("usecaseCategoryId", "usecaseSubcategoryId"); + +-- CreateIndex +CREATE UNIQUE INDEX "UsecaseCategory_name_key" ON "UsecaseCategory"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "UsecaseSubcategory_name_key" ON "UsecaseSubcategory"("name"); + +-- AddForeignKey +ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Template" ADD CONSTRAINT "Template_usecaseSubcategoryId_fkey" FOREIGN KEY ("usecaseSubcategoryId") REFERENCES "UsecaseSubcategory"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Usecase" ADD CONSTRAINT "Usecase_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "UsecaseSubcategory" ADD CONSTRAINT "UsecaseSubcategory_usecaseCategoryId_fkey" FOREIGN KEY ("usecaseCategoryId") REFERENCES "UsecaseCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/deeplink-generator/seeding/seed.js b/deeplink-generator/seeding/seed.js index e9303ad..6d41950 100644 --- a/deeplink-generator/seeding/seed.js +++ b/deeplink-generator/seeding/seed.js @@ -33,24 +33,34 @@ async function main() { } }); console.log(categorySubcategoryMap); - Object.keys(categorySubcategoryMap).forEach(async (category) => { - const c = await prisma.usecaseCategory.create({ - data: { - name: category, - }, - }); - const sc = await prisma.usecaseSubcategory.createMany({ - data: categorySubcategoryMap[category].map((subCategory) => ({ - name: subCategory, - usecaseCategoryId: c.id, - })), - }); - console.log("Seeded Subcategory", sc); - }); + const createdCategories = await Promise.all( + Object.keys(categorySubcategoryMap).map((category) => + prisma.usecaseCategory.create({ + data: { + name: category, + UsecaseSubcategory: { + createMany: { + data: categorySubcategoryMap[category].map((name) => ({ + name, + })), + }, + }, + }, + include: { + UsecaseSubcategory: true, + } + }) + ) + ); + console.log("Seeded Categories", createdCategories); const seededCategories = await prisma.usecaseCategory.findMany(); const seededSubCategories = await prisma.usecaseSubcategory.findMany(); + console.log("Seeded Categories", seededCategories); + console.log("Seeded SubCategories", seededSubCategories); + + console.log("Seeded Categories", seededCategories); console.log("Seeded SubCategories", seededSubCategories); @@ -65,7 +75,7 @@ async function main() { category.name.toLowerCase() === file.category.toLowerCase() )[0].id ); - + console.log( "FILTERED sub-category", file.subCategory.toLowerCase(), diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx index b2fe72b..62c342b 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx @@ -33,7 +33,6 @@ const GenerateDeepLinkPage = async ({ const templateId = (await params).templateId; const template = await getTemplateById(templateId); const templateValue = flattenTemplate(template!.value); - const handleSubmit = async (form: FormData) => { "use server"; const value = formDataToFormItemArray(form); @@ -50,7 +49,10 @@ const GenerateDeepLinkPage = async ({ templateId, value: value.map(({ name, value }) => { try { - JSON.parse(value); + const v = JSON.parse(value); + if (typeof v !== "object") { + throw new Error("Primitive values as strings are not allowed"); + } return { name, value: `{{${name}}}` }; } catch { return { name, value }; diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx index 69a4eae..916604d 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx @@ -67,7 +67,6 @@ const PublishDeepLinkPage = async ({ justifyContent="flex-start" > -     @@ -78,7 +77,6 @@ const PublishDeepLinkPage = async ({ justifyContent="flex-start" > -     @@ -89,7 +87,6 @@ const PublishDeepLinkPage = async ({ justifyContent="flex-start" > -     @@ -118,11 +115,13 @@ const PublishDeepLinkPage = async ({ + {/* {JSON.stringify(usecase?.value)} */} + { .filter( (key) => !( - (flattenedUsecase[key] as string).startsWith("{{") && - (flattenedUsecase[key] as string).endsWith("}}") + (flattenedUsecase[key]!.toString()).startsWith("{{") && + (flattenedUsecase[key]!.toString()).endsWith("}}") ) ) .map((key: string) => ( diff --git a/deeplink-generator/src/app/favicon.ico b/deeplink-generator/src/app/favicon.ico index 718d6fea4835ec2d246af9800eddb7ffb276240c..53699d92548b52747342633e62699a1e84b13f65 100644 GIT binary patch literal 2042 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 LrJugUA?W`A8`#=m diff --git a/deeplink-generator/src/app/ondc_logo.png b/deeplink-generator/src/app/ondc_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..979e470a491454925e4f043851c8e5dab414c0b6 GIT binary patch literal 41615 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 literal 0 HcmV?d00001 diff --git a/deeplink-generator/src/app/utils/inflate.ts b/deeplink-generator/src/app/utils/inflate.ts index 61af218..85430b4 100644 --- a/deeplink-generator/src/app/utils/inflate.ts +++ b/deeplink-generator/src/app/utils/inflate.ts @@ -94,6 +94,5 @@ export function inflateDeepLink(formData: FormItem[]) { formData.forEach((item) => { setNestedValue(result, item.name, item.value); }); - return result; } From 03a325b82df722fe994b0184e703887b6aad46ef Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Mon, 13 Jan 2025 13:09:09 +0530 Subject: [PATCH 44/49] Removing caching for docker. --- .github/workflows/app-deployment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 9d44420..24e3d91 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -37,7 +37,7 @@ jobs: echo "STARTING build" sudo cp docker-compose.dev.yml docker-compose.yml - sudo docker compose up ondc_deep_link_app -d --build + sudo docker compose up ondc_deep_link_app -d --build --no-cache sudo docker system prune -f From 7f2ee35f4fa81b8e292922397bac215ce7557b3f Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Mon, 13 Jan 2025 13:11:50 +0530 Subject: [PATCH 45/49] bug fix. --- .github/workflows/app-deployment.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml index 24e3d91..ffc7817 100644 --- a/.github/workflows/app-deployment.yml +++ b/.github/workflows/app-deployment.yml @@ -37,7 +37,8 @@ jobs: echo "STARTING build" sudo cp docker-compose.dev.yml docker-compose.yml - sudo docker compose up ondc_deep_link_app -d --build --no-cache + sudo docker compose build --no-cache ondc_deep_link_app && sudo docker compose up -d ondc_deep_link_app + sudo docker system prune -f From f3f004d3666d1586ae6e8541f4c648f6306f404e Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Tue, 14 Jan 2025 10:54:47 +0530 Subject: [PATCH 46/49] Breadcrumbs. --- .../deep-link/filter/[categoryId]/page.tsx | 8 ++++- .../src/app/(deep-link)/deep-link/page.tsx | 7 ++-- .../deep-link/usecases/browse/page.tsx | 13 ++++--- .../usecases/create/[templateId]/page.tsx | 17 ++++++++-- .../usecases/publish/[deepLinkId]/page.tsx | 4 +-- .../src/app/components/CustomHeading.tsx | 34 +++++++++++++++++-- 6 files changed, 69 insertions(+), 14 deletions(-) diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/filter/[categoryId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/filter/[categoryId]/page.tsx index d7403e7..5c59598 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/filter/[categoryId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/filter/[categoryId]/page.tsx @@ -24,7 +24,13 @@ const SelectUsecaseSubcategory = async ({ const subcategories = await getUsecaseSubcategories(categoryId); return ( <> - + { const categories = await getUsecaseCategories(); return ( <> - + ; }) => { const { category, subcategory } = await searchParams; - console.log("category", category); - console.log("subcategory", subcategory); const templates = await getTemplateByCategoryAndSubcategory( category, subcategory @@ -37,13 +35,20 @@ const SelectUsecasePage = async ({ const handleSelection = async (form: FormData) => { "use server"; - redirect(`/deep-link/usecases/create/${form.get("templateId")}`); + redirect( + `/deep-link/usecases/create/${form.get( + "templateId" + )}?category=${category}&subcategory=${subcategory}` + ); }; return ( <> - + {Array.isArray(templates) && templates.length > 0 ? (
diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx index 62c342b..6e8c643 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/create/[templateId]/page.tsx @@ -27,10 +27,13 @@ import { import { redirect } from "next/navigation"; const GenerateDeepLinkPage = async ({ params, + searchParams, }: { params: Promise<{ templateId: string }>; + searchParams: Promise<{ category: string; subcategory: string }>; }) => { const templateId = (await params).templateId; + const { category } = await searchParams; const template = await getTemplateById(templateId); const templateValue = flattenTemplate(template!.value); const handleSubmit = async (form: FormData) => { @@ -60,12 +63,22 @@ const GenerateDeepLinkPage = async ({ }), }); - redirect(`/deep-link/usecases/publish/${deepLink.id}`); + redirect(`/deep-link/usecases/publish/${deepLink.id}?templateId=${templateId}`); } }; return ( <> - + - + {/* Save Private - + */} Publish to server diff --git a/deeplink-generator/src/app/components/CustomHeading.tsx b/deeplink-generator/src/app/components/CustomHeading.tsx index b7c7613..45ec191 100644 --- a/deeplink-generator/src/app/components/CustomHeading.tsx +++ b/deeplink-generator/src/app/components/CustomHeading.tsx @@ -1,15 +1,43 @@ -import { Paper, Stack, Typography } from "@mui/material"; +import { Box, Breadcrumbs, Paper, Stack, Typography } from "@mui/material"; import React from "react"; import { ThemeTogglerButton } from "./ThemeTogglerButton"; +import Link from "next/link"; type CustomHeadingProps = { heading: string; + breadcrumb?: Array<{ + name: string; + link: string; + }>; }; -export const CustomHeading = ({ heading }: CustomHeadingProps) => { +export const CustomHeading = ({ heading, breadcrumb }: CustomHeadingProps) => { return ( + {breadcrumb && ( + + + {breadcrumb.map((each, index) => ( + + + {each.name} + + + ))} + + {heading} + + + + )} + - {heading} + + {heading} + From f201844d022d886aad4a618e6f0114ee60ce9639 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Tue, 14 Jan 2025 17:38:46 +0530 Subject: [PATCH 47/49] New migrations,UI changes and bug fixes. --- .../migration.sql | 0 .../20250114110040_deep_link/migration.sql | 6 ++++ deeplink-generator/prisma/schema.prisma | 1 + .../usecases/publish/[deepLinkId]/page.tsx | 19 +++++++---- .../usecases/thank-you/[deepLinkId]/page.tsx | 4 +-- .../src/app/actions/publish-usecase.ts | 14 +++++++- .../src/app/components/CustomHeading.tsx | 34 +++++++++---------- 7 files changed, 51 insertions(+), 27 deletions(-) rename deeplink-generator/prisma/migrations/{20250113062321_init_13_01_25 => 20250114102601_init_14_01_25}/migration.sql (100%) create mode 100644 deeplink-generator/prisma/migrations/20250114110040_deep_link/migration.sql diff --git a/deeplink-generator/prisma/migrations/20250113062321_init_13_01_25/migration.sql b/deeplink-generator/prisma/migrations/20250114102601_init_14_01_25/migration.sql similarity index 100% rename from deeplink-generator/prisma/migrations/20250113062321_init_13_01_25/migration.sql rename to deeplink-generator/prisma/migrations/20250114102601_init_14_01_25/migration.sql diff --git a/deeplink-generator/prisma/migrations/20250114110040_deep_link/migration.sql b/deeplink-generator/prisma/migrations/20250114110040_deep_link/migration.sql new file mode 100644 index 0000000..d596b11 --- /dev/null +++ b/deeplink-generator/prisma/migrations/20250114110040_deep_link/migration.sql @@ -0,0 +1,6 @@ +-- AlterTable +ALTER TABLE "Template" ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); + +-- AlterTable +ALTER TABLE "Usecase" ADD COLUMN "usecaseDeepLink" TEXT, +ALTER COLUMN "id" SET DEFAULT substr(gen_random_uuid()::text, 1, 16); diff --git a/deeplink-generator/prisma/schema.prisma b/deeplink-generator/prisma/schema.prisma index 3228c54..794cb58 100644 --- a/deeplink-generator/prisma/schema.prisma +++ b/deeplink-generator/prisma/schema.prisma @@ -57,6 +57,7 @@ model Usecase { description String? creatorName String? qrPdfLink String? + usecaseDeepLink String? usecaseStage UsecaseStage } diff --git a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx index 185d092..8c9a69d 100644 --- a/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx +++ b/deeplink-generator/src/app/(deep-link)/deep-link/usecases/publish/[deepLinkId]/page.tsx @@ -33,6 +33,8 @@ const PublishDeepLinkPage = async ({ }) => { const deepLinkId = (await params).deepLinkId; const usecase = await getUsecaseById(deepLinkId); + if (!usecase || usecase.usecaseStage !== UsecaseStage.DRAFT) + redirect("/deep-link"); const handleFormSubmit = async (formData: FormData) => { "use server"; const form = formDataToEntry(formData); @@ -77,7 +79,7 @@ const PublishDeepLinkPage = async ({ justifyContent="flex-start" > - + - + @@ -116,12 +123,12 @@ const PublishDeepLinkPage = async ({ - - - {/* {JSON.stringify(usecase?.value)} */} + + + {/* {JSON.stringify(usecase?.value)} */} + - QR - + { return ( {breadcrumb && ( - - - {breadcrumb.map((each, index) => ( - - - {each.name} - - - ))} - - {heading} - - - + + {breadcrumb.map((each, index) => ( + + + {each.name} + + + ))} + + {heading} + + )} From ed06049d726c800a831ceb02167bba75177b4439 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Tue, 14 Jan 2025 18:57:01 +0530 Subject: [PATCH 48/49] Removing caching from migrations runner. --- .github/workflows/db-operations.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml index cbe58dd..5583228 100644 --- a/.github/workflows/db-operations.yml +++ b/.github/workflows/db-operations.yml @@ -22,7 +22,7 @@ jobs: sleep 10 - sudo docker build -t migrations-runner -f Dockerfile.migration . + sudo docker build --no-cache -t migrations-runner -f Dockerfile.migration . sudo docker run --rm \ --network deeplink-generator_default \ migrations-runner From 24726abfcc9e6d92eeed5b5b1c054b9ca650d417 Mon Sep 17 00:00:00 2001 From: Abhik Banerjee Date: Wed, 15 Jan 2025 10:31:17 +0530 Subject: [PATCH 49/49] Build fix. --- deeplink-generator/src/app/components/CustomHeading.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deeplink-generator/src/app/components/CustomHeading.tsx b/deeplink-generator/src/app/components/CustomHeading.tsx index f4c52f4..20370ce 100644 --- a/deeplink-generator/src/app/components/CustomHeading.tsx +++ b/deeplink-generator/src/app/components/CustomHeading.tsx @@ -1,5 +1,4 @@ -import { Box, Breadcrumbs, Paper, Stack, Typography } from "@mui/material"; -import { Box, Breadcrumbs, Paper, Stack, Typography } from "@mui/material"; +import { Breadcrumbs, Paper, Stack, Typography } from "@mui/material"; import React from "react"; import { ThemeTogglerButton } from "./ThemeTogglerButton"; import Link from "next/link";