refactor(tender-details): enhance translation and summary features
- Integrated language selection and translation functionality in TenderDetails, allowing users to choose display language and translate tender metadata. - Added AI summary section to display overall summary of the tender. - Updated API service to support translation requests and handle responses effectively. - Improved pagination handling in AdminsTable for better user experience. - Refactored various components to ensure consistent data flow and state management.
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import Loading from "@/components/loading";
|
||||
import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb";
|
||||
import CreateAdminForm from "@/components/forms/admins/CreateAdmin";
|
||||
import Loading from "@/components/loading";
|
||||
import { useAdminDetails } from "@/hooks/queries";
|
||||
import React, { use } from "react";
|
||||
import { use } from "react";
|
||||
|
||||
const AdminEditPage = ({ params }: { params: Promise<{ id: string }> }) => {
|
||||
const { id } = use(params);
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
"use client";
|
||||
import { LocationIcon } from "@/assets/icons";
|
||||
import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb";
|
||||
import { Select } from "@/components/FormElements/select";
|
||||
import { ShowcaseSection } from "@/components/Layouts/showcase-section";
|
||||
import Loading from "@/components/loading";
|
||||
import Status from "@/components/ui/Status";
|
||||
import { useTenderDetailQuery } from "@/hooks/queries";
|
||||
import { Button } from "@/components/ui-elements/button";
|
||||
import { Languages } from "@/constants/languages";
|
||||
import {
|
||||
useTenderDetailQuery,
|
||||
useTranslateTenderMutation,
|
||||
} from "@/hooks/queries";
|
||||
import { useGetFlagQuery } from "@/hooks/queries/useFlagsQueries";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
@@ -14,6 +20,7 @@ import {
|
||||
} from "@/utils/shared";
|
||||
import getSymbolFromCurrency from "currency-symbol-map";
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { use } from "react";
|
||||
|
||||
interface IProps {
|
||||
@@ -64,7 +71,15 @@ const SHOWCASE_CENTERED_BODY = {
|
||||
|
||||
const TenderDetails = ({ params }: IProps) => {
|
||||
const { details } = use(params);
|
||||
const { data, isLoading, isError } = useTenderDetailQuery(details);
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const selectedLanguage = searchParams.get("lang") || "en";
|
||||
const { mutate: translateTender, isPending: isTranslating } =
|
||||
useTranslateTenderMutation();
|
||||
const { data, isLoading, isError, refetch } = useTenderDetailQuery(details, {
|
||||
lang: selectedLanguage,
|
||||
});
|
||||
const countryCodeNormalized =
|
||||
data?.data.country_code?.trim().toUpperCase() ?? "";
|
||||
const hasValidCountryCodeForDisplay =
|
||||
@@ -130,21 +145,71 @@ const TenderDetails = ({ params }: IProps) => {
|
||||
{tender.title}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex shrink-0 flex-col gap-3 sm:flex-row sm:items-center sm:justify-end">
|
||||
<Status status={tender.status ?? "Unknown"}>
|
||||
{tender.status}
|
||||
</Status>
|
||||
{submissionUrl && (
|
||||
<Link
|
||||
href={submissionUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="shadow-theme-xs inline-flex items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2.5 text-sm font-medium text-white transition hover:bg-primary/90 hover:shadow-md"
|
||||
>
|
||||
<span>Open submission</span>
|
||||
<ExternalLinkIcon className="opacity-90" />
|
||||
</Link>
|
||||
)}
|
||||
<div className="flex w-full min-w-0 shrink-0 flex-col gap-3 lg:max-w-2xl lg:items-end">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-dark-5 dark:text-dark-6 lg:w-full lg:text-end">
|
||||
Language & translation
|
||||
</p>
|
||||
<div className="flex w-full flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:justify-end">
|
||||
<Select
|
||||
name="lang"
|
||||
label="Display language"
|
||||
items={Languages}
|
||||
className={cn(
|
||||
"w-full shrink-0 space-y-0 sm:w-[min(100%,11.5rem)]",
|
||||
"[&_label]:sr-only",
|
||||
)}
|
||||
value={selectedLanguage}
|
||||
onChange={async (event) => {
|
||||
const nextParams = new URLSearchParams(
|
||||
searchParams.toString(),
|
||||
);
|
||||
const nextLanguage = event.target.value;
|
||||
if (nextLanguage) {
|
||||
nextParams.set("lang", nextLanguage);
|
||||
} else {
|
||||
nextParams.delete("lang");
|
||||
}
|
||||
const queryString = nextParams.toString();
|
||||
router.replace(
|
||||
queryString ? `${pathname}?${queryString}` : pathname,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
size="small"
|
||||
shape="rounded"
|
||||
className="w-full shrink-0 whitespace-nowrap sm:w-auto sm:min-h-[3.375rem]"
|
||||
label={isTranslating ? "Translating..." : "Translate metadata"}
|
||||
disabled={isTranslating}
|
||||
onClick={() => {
|
||||
translateTender(
|
||||
{ id: details, language: selectedLanguage },
|
||||
{
|
||||
onSuccess: async () => {
|
||||
await refetch();
|
||||
},
|
||||
},
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div className="flex shrink-0 items-center sm:min-h-[3.375rem]">
|
||||
<Status status={tender.status ?? "Unknown"}>
|
||||
{tender.status}
|
||||
</Status>
|
||||
</div>
|
||||
{submissionUrl && (
|
||||
<Link
|
||||
href={submissionUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="shadow-theme-xs inline-flex min-h-[3.375rem] w-full shrink-0 items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2.5 text-sm font-medium text-white transition hover:bg-primary/90 hover:shadow-md sm:w-auto"
|
||||
>
|
||||
<span>Open submission</span>
|
||||
<ExternalLinkIcon className="opacity-90" />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -332,6 +397,20 @@ const TenderDetails = ({ params }: IProps) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<SectionHeading>Translation</SectionHeading>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div className="rounded-2xl border border-stroke/60 bg-gray-2/40 p-6 shadow-inner dark:border-dark-3 dark:bg-dark-3/30">
|
||||
<p className="mb-2 text-xs font-semibold uppercase tracking-wide text-dark-5 dark:text-dark-6">
|
||||
Resolved language
|
||||
</p>
|
||||
<p className="text-sm text-dark dark:text-white">
|
||||
{tender.language || "original"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<SectionHeading>Description</SectionHeading>
|
||||
<div className="rounded-2xl border border-stroke/60 bg-gray-2/40 p-6 shadow-inner dark:border-dark-3 dark:bg-dark-3/30">
|
||||
@@ -340,6 +419,15 @@ const TenderDetails = ({ params }: IProps) => {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<SectionHeading>AI summary</SectionHeading>
|
||||
<div className="rounded-2xl border border-stroke/60 bg-gray-2/40 p-6 shadow-inner dark:border-dark-3 dark:bg-dark-3/30">
|
||||
<p className="whitespace-pre-wrap text-sm leading-relaxed text-dark dark:text-white/90">
|
||||
{tender.overall_summary || "Summary not ready yet"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShowcaseSection>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user