diff --git a/.github/workflows/app-deployment.yml b/.github/workflows/app-deployment.yml
new file mode 100644
index 0000000..9d44420
--- /dev/null
+++ b/.github/workflows/app-deployment.yml
@@ -0,0 +1,47 @@
+name: App Deployment
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+ workflow_dispatch:
+
+jobs:
+ deploy-app:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Deploy Generator App
+ 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" ] && sudo rm -rf "deeplink-resolver-server"
+ sudo git clone https://github.com/abhik-wil/deeplink-resolver-server.git
+ cd deeplink-resolver-server/deeplink-generator
+
+
+ echo "STARTING Deployment"
+ sudo touch .env
+ # Create backend .env file
+ 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 "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
+ sudo docker system prune -f
+
+
+ echo "BUILD Complete; Restarting Nginx"
+ sudo systemctl restart nginx
+ echo "Deployment COMPLETE"
+
diff --git a/.github/workflows/db-operations.yml b/.github/workflows/db-operations.yml
new file mode 100644
index 0000000..cbe58dd
--- /dev/null
+++ b/.github/workflows/db-operations.yml
@@ -0,0 +1,29 @@
+name: Database Operations
+on:
+ workflow_dispatch:
+
+jobs:
+ db-reset-and-migrate:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Execute Generator DB Operations
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.EC2_HOST }}
+ username: ${{ secrets.EC2_USERNAME }}
+ key: ${{ secrets.EC2_PEM }}
+ port: 22
+ script: |
+ cd deeplink-resolver-server/deeplink-generator
+
+ echo "DB Teardown and Restart"
+ sudo docker compose down ondc_deep_link_db
+ sudo docker compose up -d ondc_deep_link_db
+
+ sleep 10
+
+ sudo docker build -t migrations-runner -f Dockerfile.migration .
+ sudo docker run --rm \
+ --network deeplink-generator_default \
+ migrations-runner
+
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/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
new file mode 100644
index 0000000..3d01240
--- /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
\ No newline at end of file
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/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/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;
diff --git a/deeplink-generator/package-lock.json b/deeplink-generator/package-lock.json
index 266499b..f0db430 100644
--- a/deeplink-generator/package-lock.json
+++ b/deeplink-generator/package-lock.json
@@ -13,10 +13,12 @@
"@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",
"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",
@@ -25,11 +27,12 @@
},
"devDependencies": {
"@types/node": "^20.17.9",
+ "@types/qrcode": "^1.5.5",
"@types/react": "^18",
"@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 +1306,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",
@@ -1470,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",
@@ -1834,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"
}
@@ -1843,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"
},
@@ -2126,6 +2278,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",
@@ -2198,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",
@@ -2289,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",
@@ -2314,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"
},
@@ -2325,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",
@@ -2499,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",
@@ -2583,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",
@@ -3266,6 +3452,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",
@@ -3501,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",
@@ -3980,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"
}
@@ -4867,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",
@@ -4904,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"
}
@@ -5001,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",
@@ -5085,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",
@@ -5269,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",
@@ -5436,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",
@@ -5642,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",
@@ -5655,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",
@@ -5761,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"
},
@@ -6119,6 +6367,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",
@@ -6250,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",
@@ -6287,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",
@@ -6307,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 070231e..06d3c72 100644
--- a/deeplink-generator/package.json
+++ b/deeplink-generator/package.json
@@ -22,10 +22,12 @@
"@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",
"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",
@@ -34,11 +36,12 @@
},
"devDependencies": {
"@types/node": "^20.17.9",
+ "@types/qrcode": "^1.5.5",
"@types/react": "^18",
"@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"
}
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/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/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 6a3c345..e9303ad 100644
--- a/deeplink-generator/seeding/seed.js
+++ b/deeplink-generator/seeding/seed.js
@@ -4,42 +4,6 @@ import path from "path";
const prisma = new PrismaClient();
-const CATEGORIES = [
- {
- name: "Retail",
- },
- {
- name: "Logistics",
- },
- {
- name: "Services",
- },
- {
- name: "Subscription",
- },
- {
- name: "IGM",
- },
-];
-
-const SUB_CATEGORIES = [
- {
- name: "Search By Item Name",
- },
- {
- name: "Search By Item Code",
- },
- {
- name: "Seller-Catalog Search",
- },
- {
- name: "Search By City",
- },
- {
- name: "Search By Category",
- },
-];
-
async function readJsonFiles() {
const currentDir = path.join(process.cwd(), "seeding");
console.log("CWD", currentDir);
@@ -57,21 +21,60 @@ async function readJsonFiles() {
}
async function main() {
- const c = await prisma.usecaseCategory.createMany({
- data: CATEGORIES,
+ 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];
+ }
});
- const sc =await prisma.usecaseSubcategory.createMany({
- data: SUB_CATEGORIES,
+ 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);
});
- 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)
+
+ 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
+ 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(
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/(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)} */}
-
;
}) => {
const deepLinkId = (await params).deepLinkId;
+ const usecase = await getUsecaseById(deepLinkId);
+ if (!usecase) {
+ redirect("/");
+ }
return (
-
+
Thank You for using ONDC, here's your deep link!
-
+
+
+
+
+
-
+
Back To Home
-
+
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/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..b9eafc6 100644
--- a/deeplink-generator/src/app/actions/publish-usecase.ts
+++ b/deeplink-generator/src/app/actions/publish-usecase.ts
@@ -2,16 +2,92 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Usecase, UsecaseStage } from "@prisma/client";
import { db } from "../../../db";
+import { Octokit } from "@octokit/rest";
+import QRCode from "qrcode";
-export async function publishUsecase(usecase: Usecase, form: FormData) {
+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, 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,
},
});
+ 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,
+ });
+
+ if (form.submissionOption === UsecaseStage.PUBLISHED) {
+ const fileName = `${updatedUsecase.id}.json`;
+ const filePath = `${FOLDER_PATH}/json/${fileName}`;
+ const content = Buffer.from(
+ JSON.stringify(updatedUsecase, null, 2)
+ ).toString("base64");
+
+ try {
+ await octokit.repos.getContent({
+ 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.OWNER_NAME_REPO || "",
+ repo: process.env.STORAGE_REPO_NAME || "",
+ 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.OWNER_NAME_REPO || "",
+ repo: process.env.STORAGE_REPO_NAME || "",
+ path: filePath,
+ message: `Add user submission ${updatedUsecase.id}`,
+ content,
+ });
+ }
+
+ 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.OWNER_NAME_REPO || "",
+ repo: process.env.STORAGE_REPO_NAME || "",
+ path: filePath,
+ message: `Update QR code for ${updatedUsecase.id}`,
+ content: qrCodeBuffer.toString("base64"),
+ });
+
return updatedUsecase;
}
diff --git a/deeplink-generator/src/app/resolver/[deepLinkId]/route.ts b/deeplink-generator/src/app/api/resolver/[deepLinkId]/route.ts
similarity index 63%
rename from deeplink-generator/src/app/resolver/[deepLinkId]/route.ts
rename to deeplink-generator/src/app/api/resolver/[deepLinkId]/route.ts
index 3788bca..c7baeb6 100644
--- a/deeplink-generator/src/app/resolver/[deepLinkId]/route.ts
+++ b/deeplink-generator/src/app/api/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);
}
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..877103a
--- /dev/null
+++ b/deeplink-generator/src/app/api/usecase/pdf/[usecaseId]/route.ts
@@ -0,0 +1,66 @@
+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", "Poster.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 = 150;
+ const x = (width - qrSize) / 2 + 20;
+ const y = (height - qrSize) / 2 + 140;
+
+ // 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=${usecase.name || "Deep Link"}.pdf`,
+ },
+ });
+ } catch (error) {
+ console.error("Error:", error);
+ return NextResponse.json(
+ { error: "Error generating PDF" },
+ { status: 500 }
+ );
+ }
+}
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],
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
+};
diff --git a/deeplink-generator/src/app/components/UsecaseEditor.tsx b/deeplink-generator/src/app/components/UsecaseEditor.tsx
index 3f6da9e..e64062f 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/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