Skip to content

Commit

Permalink
Merge pull request #238 from Sahil-Connect/SAH-119
Browse files Browse the repository at this point in the history
[SAH-119]: Nest.js Server
  • Loading branch information
Emmanuel-Melon authored Feb 10, 2025
2 parents 0375ec6 + d6a308c commit 128209e
Show file tree
Hide file tree
Showing 31 changed files with 24,071 additions and 10,706 deletions.
166 changes: 166 additions & 0 deletions .github/workflows/sahil-server.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
name: Docker image build and publish for Server
on:
workflow_dispatch:
inputs:
path_to_dockerfile:
description: Path to the dockerfile (default = 'Dockerfile')
default: "infra/docker/Dockerfile.server"
type: string
docker_build_dir:
description: Docker build directory (default = '.')
default: "."
type: string
image_tag:
description: Tag to apply to images.
type: string
default: sahil-server
lifecycle_policy_file:
description: Path to the lifecycle policy JSON file (default = 'policy.json')
default: "policy.json"
type: string
backend_s3_bucket:
description: Name of the S3bucket for Terraform backend
default: "sahil-terraform-state-bucket"
type: string
backend_iam_role:
description: Name of the Terraform backend assumable IAM Role
default: "workload-assumable-role"
type: string
github_iam_role:
description: Name of the IAM Role for adding access to ECR repo
default: "github-actions-role"
type: string
aws_account_id:
description: AWS Account ID
default: "060795911441"
type: string
aws_region:
description: Target AWS Region
default: "eu-west-1"
type: string
backend_dynamodb_table:
description: DynamoDB table for State lock
default: "sahil-terraform-table-locks"
type: string

# concurrency required to avoid terraform lock contention during ECR provisioning
concurrency: ci-${{ github.repository }}-server-docker-pipeline

jobs:
docker:
runs-on: ubuntu-latest

permissions:
id-token: write
contents: read

outputs:
image_tag: ${{ steps.build-publish.outputs.image_tag }}
version_tag: ${{ steps.build-publish.outputs.version_tag }}
full_image: ${{ steps.build-publish.outputs.full_image }}

steps:
- uses: actions/checkout@v3

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: arn:aws:iam::${{ inputs.aws_account_id }}:role/sahil-deployment-role
aws-region: ${{ inputs.aws_region }}

- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_wrapper: false

- name: prepare ECR repo name based on the Github repository
shell: bash
run: |
set -eux
# lowercase the name
repo="${GITHUB_REPOSITORY,,}"
# replace / with _
echo "ECR_REPO_NAME=${repo//\//_}" >> $GITHUB_ENV
- name: TF init
shell: bash
run: |
set -eux
terraform init -upgrade -reconfigure \
-backend-config='skip_metadata_api_check=true' \
-backend-config='skip_region_validation=true' \
-backend-config='skip_credentials_validation=true' \
-backend-config='region=${{ inputs.aws_region }}' \
-backend-config='bucket=${{ inputs.backend_s3_bucket }}' \
-backend-config='key=docker-ecr/terraform-${{ env.ECR_REPO_NAME }}.tfstate' \
-backend-config='dynamodb_table=${{ inputs.backend_dynamodb_table }}' \
-backend-config='assume_role={ role_arn = "arn:aws:iam::${{ inputs.aws_account_id }}:role/${{ inputs.backend_iam_role }}" }'
working-directory: infra/terraform

- name: Create ECR repo [TF apply]
shell: bash
run: |
set -eux
terraform apply \
-var 'repository_name=${{ env.ECR_REPO_NAME }}' \
-var 'lifecycle_policy=${{ inputs.lifecycle_policy_file }}' \
-var 'iam_role=arn:aws:iam::${{ inputs.aws_account_id }}:role/${{ inputs.github_iam_role }}' \
-var 'aws_account_id=${{ inputs.aws_account_id }}' \
-auto-approve
working-directory: infra/terraform

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
with:
registries: ${{ inputs.aws_account_id }}

- name: Build, tag, and push image to Amazon ECR
id: build-publish
shell: bash
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ env.ECR_REPO_NAME }}
IMAGE_TAG: ${{ inputs.image_tag }}
run: |
# Get the current latest image digest (if it exists)
PREVIOUS_IMAGE_MANIFEST=$(aws ecr batch-get-image \
--repository-name $ECR_REPOSITORY \
--image-ids imageTag=$IMAGE_TAG \
--output text \
--query 'images[].imageManifest' || echo "")
# Build the new image
docker build "${{ inputs.docker_build_dir }}" -f "${{ inputs.path_to_dockerfile }}" -t "$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
# Push the new image as latest
docker push "$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
# If there was a previous image, tag it as v1, v2, etc.
if [ ! -z "$PREVIOUS_IMAGE_MANIFEST" ]; then
# Get the current highest version number
CURRENT_VERSION=$(aws ecr describe-images \
--repository-name $ECR_REPOSITORY \
--query 'imageDetails[].imageTags[?starts_with(@, `v`)]' \
--output text | grep -o 'v[0-9]*' | sed 's/v//' | sort -n | tail -1)
# Calculate next version number
if [ -z "$CURRENT_VERSION" ]; then
NEW_VERSION="v1"
else
NEW_VERSION="v$((CURRENT_VERSION + 1))"
fi
# Tag the previous image with the new version
aws ecr put-image \
--repository-name $ECR_REPOSITORY \
--image-tag $NEW_VERSION \
--image-manifest "$PREVIOUS_IMAGE_MANIFEST"
echo "Previous image tagged as $NEW_VERSION"
echo "version_tag=$NEW_VERSION" >> $GITHUB_OUTPUT
fi
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
echo "full_image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
2 changes: 1 addition & 1 deletion apps/admin/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
216 changes: 216 additions & 0 deletions apps/agent/src/components/PromptOrderSuggestion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import { useState } from 'react'
import { Button, Modal, Drawer } from 'ui'
import { HiOutlineShoppingCart, HiOutlineSparkles } from 'react-icons/hi2'

interface Item {
id: string
name: string
quantity: number
unit: string
price: number
}

interface Supplier {
name: string
rating: number
totalOrders: number
onTimeDelivery: number
}

interface PromptOrderSuggestionProps {
suggestion: {
items: Item[]
supplier: Supplier
deliveryDate: string
totalCost: number
}
onCompare: () => void
}

// Mock suppliers data
const mockSuppliers = [
{
id: 1,
name: "Global Foods Ltd",
rating: 4.8,
totalOrders: 1500,
onTimeDelivery: 98,
price: 42.50,
deliveryDate: "2025-01-10",
minOrderQuantity: 25
},
{
id: 2,
name: "Fresh Direct",
rating: 4.6,
totalOrders: 1200,
onTimeDelivery: 95,
price: 40.00,
deliveryDate: "2025-01-12",
minOrderQuantity: 20
},
{
id: 3,
name: "Premium Suppliers",
rating: 4.9,
totalOrders: 2000,
onTimeDelivery: 99,
price: 45.00,
deliveryDate: "2025-01-09",
minOrderQuantity: 30
}
]

interface SupplierCompareDrawerProps {
currentSupplier: Supplier
}

function SupplierCompareDrawer({ currentSupplier }: SupplierCompareDrawerProps) {
return (
<Drawer id="supplier-compare" CTA="Compare" className="justify-between space-y-4">
<div className="space-y-1 mb-6">
<h3 className="font-bold text-xl">Compare Suppliers</h3>
<p className="opacity-80">
Compare different suppliers and choose the best option for your order.
</p>
</div>

<div className="grid gap-4">
{mockSuppliers.map((supplier) => (
<div
key={supplier.id}
className="card card-bordered p-4 relative hover:shadow-md transition-shadow"
>
<div className="flex justify-between items-start">
<div>
<h3 className="font-medium text-lg">{supplier.name}</h3>
<div className="mt-2 space-y-1 text-sm text-gray-600">
<p className="flex items-center">
<span className="mr-1"></span>
{supplier.rating} ({supplier.totalOrders} orders)
</p>
<p className="flex items-center">
<HiOutlineSparkles className="mr-1 h-4 w-4 text-green-500" />
{supplier.onTimeDelivery}% On-time delivery
</p>
</div>
</div>
<div className="text-right">
<p className="text-lg font-semibold">${supplier.price}</p>
<p className="text-sm text-gray-500">Min. order: {supplier.minOrderQuantity} units</p>
</div>
</div>
<div className="mt-4">
<p className="text-sm text-gray-600">Delivery by: {supplier.deliveryDate}</p>
</div>
{supplier.name === currentSupplier.name && (
<div className="absolute top-2 right-2">
<span className="px-2 py-1 bg-green-100 text-green-800 text-xs rounded-full">
Current Supplier
</span>
</div>
)}
<div className="mt-4">
<Button className="w-full" variant={supplier.name === currentSupplier.name ? "outline" : "primary"}>
{supplier.name === currentSupplier.name ? 'Current Selection' : 'Select Supplier'}
</Button>
</div>
</div>
))}
</div>
</Drawer>
)
}

export function PromptOrderSuggestion({ suggestion, onCompare }: PromptOrderSuggestionProps) {
const [isEditModalOpen, setIsEditModalOpen] = useState(false)
const [editedItem, setEditedItem] = useState<Item | null>(null)

const handleEditItem = (item: Item) => {
setEditedItem(item)
setIsEditModalOpen(true)
}

const handleSaveEdit = () => {
console.log('Saving edited item:', editedItem)
setIsEditModalOpen(false)
}

return (
<div className="p-6">
<h3 className="text-xl font-semibold mb-6">Suggested Order</h3>
<p className="text-gray-500 text-sm mb-4">Based on your request, here is what we suggest:</p>

<div className="bg-gray-50 rounded-lg p-4 mb-6">
<h4 className="text-lg font-medium mb-2">Supplier Information</h4>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<p className="text-sm text-gray-500">Supplier</p>
<p className="font-medium">{suggestion.supplier.name}</p>
</div>
<div>
<p className="text-sm text-gray-500">Rating</p>
<p className="font-medium"> {suggestion.supplier.rating}</p>
</div>
<div>
<p className="text-sm text-gray-500">Total Orders</p>
<p className="font-medium">{suggestion.supplier.totalOrders}</p>
</div>
<div>
<p className="text-sm text-gray-500">On-Time Delivery</p>
<p className="font-medium">{suggestion.supplier.onTimeDelivery}%</p>
</div>
</div>
</div>

<div className="mb-4">
<p className="text-sm text-gray-500 mb-2">Delivery Date</p>
<p className="font-medium">{suggestion.deliveryDate}</p>
</div>

<table className="w-full mb-6">
<thead className="bg-gray-50">
<tr>
<th className="text-left p-2">Item</th>
<th className="text-left p-2">Quantity</th>
<th className="text-right p-2">Price</th>
<th className="text-right p-2">Actions</th>
</tr>
</thead>
<tbody>
{suggestion.items.map((item) => (
<tr key={item.id} className="border-b">
<td className="p-2">{item.name}</td>
<td className="p-2">{item.quantity} {item.unit}</td>
<td className="text-right p-2">${item.price.toFixed(2)}</td>
<td className="text-right p-2">
<Button
variant="outline"
size="sm"
onClick={() => handleEditItem(item)}
>
Edit
</Button>
</td>
</tr>
))}
<tr className="font-medium">
<td colSpan={2} className="p-2 text-right">Total:</td>
<td className="p-2 text-right">${suggestion.totalCost.toFixed(2)}</td>
<td></td>
</tr>
</tbody>
</table>

<div className="flex justify-between">
<SupplierCompareDrawer currentSupplier={suggestion.supplier} />
<Button>
<HiOutlineShoppingCart className="h-4 w-4 mr-2" />
Place Order
</Button>
</div>


</div>
)
}
Loading

0 comments on commit 128209e

Please sign in to comment.