diff --git a/next.config.ts b/next.config.ts index 20873fc..d6ec84f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,17 @@ -import type {NextConfig} from "next"; +import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ + images: { + remotePatterns: [ + { + protocol: "http", + hostname: "localhost", + port: "4000", + pathname: "/uploads/**", + }, + ], + }, }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 05a7a75..9e4c2bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "frontend", "version": "0.1.0", "dependencies": { + "@tanstack/react-query": "^5.90.21", + "axios": "^1.13.5", "next": "15.5.4", "react": "19.1.0", "react-dom": "19.1.0", @@ -1433,6 +1435,32 @@ "tailwindcss": "4.1.14" } }, + "node_modules/@tanstack/query-core": { + "version": "5.90.20", + "resolved": "https://mirror-npm.runflare.com/@tanstack/query-core/-/query-core-5.90.20.tgz", + "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.21", + "resolved": "https://mirror-npm.runflare.com/@tanstack/react-query/-/react-query-5.90.21.tgz", + "integrity": "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -2266,6 +2294,12 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://mirror-npm.runflare.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2290,6 +2324,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.13.5", + "resolved": "https://mirror-npm.runflare.com/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2363,7 +2408,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -2477,6 +2521,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://mirror-npm.runflare.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2674,6 +2730,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://mirror-npm.runflare.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -2708,7 +2773,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -2817,7 +2881,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2826,7 +2889,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2862,7 +2924,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -2874,7 +2935,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -3457,6 +3517,26 @@ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://mirror-npm.runflare.com/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3472,6 +3552,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://mirror-npm.runflare.com/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3531,7 +3627,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -3555,7 +3650,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -3637,7 +3731,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3709,7 +3802,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3721,7 +3813,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -4621,7 +4712,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -4653,6 +4743,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://mirror-npm.runflare.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://mirror-npm.runflare.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5126,6 +5237,12 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://mirror-npm.runflare.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package.json b/package.json index 43eb4a8..49b011b 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "app": "npm run json-server & npm run dev" }, "dependencies": { + "@tanstack/react-query": "^5.90.21", + "axios": "^1.13.5", "next": "15.5.4", "react": "19.1.0", "react-dom": "19.1.0", diff --git a/public/dr-emami.webp b/public/dr-emami.webp new file mode 100644 index 0000000..ca8291f Binary files /dev/null and b/public/dr-emami.webp differ diff --git a/public/dr-fahimeh-nosrati.webp b/public/dr-fahimeh-nosrati.webp new file mode 100644 index 0000000..9806403 Binary files /dev/null and b/public/dr-fahimeh-nosrati.webp differ diff --git a/public/dr-galin.webp b/public/dr-galin.webp new file mode 100644 index 0000000..25db3d9 Binary files /dev/null and b/public/dr-galin.webp differ diff --git a/public/dr-heydarnejad.webp b/public/dr-heydarnejad.webp new file mode 100644 index 0000000..53686ad Binary files /dev/null and b/public/dr-heydarnejad.webp differ diff --git a/public/dr-majd.webp b/public/dr-majd.webp new file mode 100644 index 0000000..354af16 Binary files /dev/null and b/public/dr-majd.webp differ diff --git a/public/dr-rojaei.webp b/public/dr-rojaei.webp new file mode 100644 index 0000000..89ff2cc Binary files /dev/null and b/public/dr-rojaei.webp differ diff --git a/public/dr-t.webp b/public/dr-t.webp new file mode 100644 index 0000000..b94a6a0 Binary files /dev/null and b/public/dr-t.webp differ diff --git a/public/dr-tahmasbi.webp b/public/dr-tahmasbi.webp new file mode 100644 index 0000000..739c3fa Binary files /dev/null and b/public/dr-tahmasbi.webp differ diff --git a/public/dr-uknown.webp b/public/dr-uknown.webp new file mode 100644 index 0000000..4786c10 Binary files /dev/null and b/public/dr-uknown.webp differ diff --git a/public/khayer.webp b/public/khayer.webp new file mode 100644 index 0000000..bd752e4 Binary files /dev/null and b/public/khayer.webp differ diff --git a/public/metron-ghanbari.webp b/public/metron-ghanbari.webp new file mode 100644 index 0000000..d8b459a Binary files /dev/null and b/public/metron-ghanbari.webp differ diff --git a/public/rajabtabar.webp b/public/rajabtabar.webp new file mode 100644 index 0000000..8209d45 Binary files /dev/null and b/public/rajabtabar.webp differ diff --git a/src/app/[lang]/contact-us/page.tsx b/src/app/[lang]/contact-us/page.tsx index 012fe39..79dcfd6 100644 --- a/src/app/[lang]/contact-us/page.tsx +++ b/src/app/[lang]/contact-us/page.tsx @@ -1,24 +1,78 @@ -import {languages_types} from "@/types"; +import { languages_types } from "@/types"; import TelephoneSvg from "@/ui/components/icons/TelephoneSvg"; import PageHeaderSlider from "@/ui/page-header-slider/PageHeaderSlider"; import Image from "next/image"; import React from "react"; -import {getDictionary} from "../dictionaries"; +import { getDictionary } from "../dictionaries"; import PatientAcceptForm from "@/ui/forms/PatientAcceptForm"; -import {default_info, pages_titles, PHONE_NUMBERS} from "@/constants"; -import {Metadata} from "next"; +import { pages_titles } from "@/constants"; +import { Metadata } from "next"; export const metadata: Metadata = { title: pages_titles.contact_us["fa"] + " | " + "بیمارستان شمال", description: "Shomal Hospital IPD contact us page", }; + +interface contactUsResponseType { + status: number; + data: [ + { + email: string; + hospitalPhone: string; + mapAddress: string; + instagramLink: string | null; + ipdNumber: string | null; + translations: { + address: string; + language: { + slug: string; + title: string; + } + }[]; + } | null, + { + email: string | null; + translations: { + position: string | null; + lang: { + slug: string; + } | null; + displayName: string | null; + }[]; + } | null, + ]; + message: string; +} + +async function getData(): Promise { + const res = await fetch( + `http://localhost:3500/api/v1/public-apis/get/contact-us`, + { + cache: "no-cache", + }, + ); + + if (!res.ok) { + throw new Error("Error"); + } + + const data = await res.json(); + + return data; +} + export default async function ContactUs({ params, }: { - params: Promise<{lang: languages_types}>; + params: Promise<{ lang: languages_types }>; }) { - const {lang} = await params; - const {contact_us_page, about_page, footer, accept_request_form} = + const { lang } = await params; + const { contact_us_page, about_page, accept_request_form } = await getDictionary(lang); + + const getdata = await getData(); + const contactInfo = getdata.data.length > 0 ? getdata.data[0] : null; + + const filteredAddress = getdata.data[0]?.translations.find((item) => item?.language.slug===lang); return ( <> - {PHONE_NUMBERS.ipd_technician.href} + {contactInfo?.ipdNumber} @@ -85,10 +139,8 @@ export default async function ContactUs({ - - {default_info.email} + + {contactInfo?.email} @@ -128,7 +180,7 @@ export default async function ContactUs({ -

{footer?.contact_us?.address}

+

{filteredAddress?.address}

@@ -147,8 +199,8 @@ export default async function ContactUs({
- - {default_info.email} + + {contactInfo?.email}
@@ -160,25 +212,25 @@ export default async function ContactUs({
- {PHONE_NUMBERS.ipd_technician.href} + {contactInfo?.ipdNumber} | - {PHONE_NUMBERS.hospital.label} + {contactInfo?.hospitalPhone}
@@ -196,15 +248,14 @@ export default async function ContactUs({ -
+ src="/main-logo.png" + fill + alt="shomal hospital" + style={{ + color: "transparent", + fill: "red", + objectFit: "contain", + }} + /> +
+

+ {filteredData?.underLogoText} +

+ +
+
+

{dict.related_links}

+
    + {FooterMenuLinks1.map((link) => ( +
  • + + + +
  • + ))} +
+
+
+
+
+

+ {footer?.contact_us_text} +

+

+ + {footer?.our_address} + {" "} + :{filteredData?.address} +

+

+ + {footer?.ipd_technician_number} + {" "} + :   + + + {getdata.data.ipdNumber} + + +

+

+ + {footer?.hospital_number} + {" "} + :   + + + {getdata.data.hospitalPhone} + + +

+
+ +
- - - + + + ); diff --git a/src/app/[lang]/medical-services/page.tsx b/src/app/[lang]/medical-services/page.tsx index 43d6bf1..96c0e32 100644 --- a/src/app/[lang]/medical-services/page.tsx +++ b/src/app/[lang]/medical-services/page.tsx @@ -1,7 +1,6 @@ import PageHeaderSlider from "@/ui/page-header-slider/PageHeaderSlider"; import Link from "next/link"; import React, {lazy, Suspense} from "react"; -import {packages_types} from "@/types"; import {getDictionary} from "../dictionaries"; import {Metadata} from "next"; import {pages_titles} from "@/constants"; diff --git a/src/app/[lang]/page.tsx b/src/app/[lang]/page.tsx index ba6206d..dfedf01 100644 --- a/src/app/[lang]/page.tsx +++ b/src/app/[lang]/page.tsx @@ -2,15 +2,88 @@ import React from "react"; import PageHeaderSlider from "@/ui/page-header-slider/PageHeaderSlider"; import Image from "next/image"; import PatientConsultantForm from "@/ui/forms/PatientConsultantForm"; -import {getDictionary} from "./dictionaries"; +import { getDictionary } from "./dictionaries"; +import { languages_types } from "@/types"; + +interface aboutUsTextResponse { + status: number; + data: { + translations: { aboutUsText: string; language: { slug: string } }[]; + }; + message: string; +} + +interface departmentdataResponse { + status: number; + data: { + translations: { + firstName: string | null; + lastName: string | null; + position: string | null; + excerpt: string | null; + lang: { + slug: string; + } | null; + }[]; + image: { + fileUrl: string; + }; + }[]; + message: string; +} +async function getAboutUsText(): Promise { + const res = await fetch( + `${process.env.API_URL}/public-apis/get/about-us-text`, + // { next: { revalidate: 60 } }, + { cache: "no-cache" }, + ); + + if (!res.ok) { + return { status: 200, data: { translations: [] }, message: "Ok" }; + } + + const data = await res.json(); + + return data; +} + +async function getDepartmentMembers(): Promise { + const res = await fetch( + `${process.env.API_URL}/public-apis/get/department-members`, + // { next: { revalidate: 60 } }, + { cache: "no-cache" }, + ); + + if (!res.ok) { + return { + status: 200, + data: [{ translations: [], image: { fileUrl: "#" } }], + message: "Ok", + }; + } + + const data = await res.json(); + + return data; +} export default async function Page({ params, }: { - params: Promise<{lang: "en" | "fa" | "ar"}>; + params: Promise<{ lang: string }>; }) { - const {lang} = await params; - const {about_page} = await getDictionary(lang); + const { lang } = await params; + const { about_page } = await getDictionary(lang as languages_types); + const getdata = await getAboutUsText(); + const getmembers = await getDepartmentMembers(); + + const filteredTranslations = getdata?.data?.translations.find( + (item) => item.language.slug === lang, + ); + + // const filteredDepartmentMembersTranslations = getmembers?.data?.flatMap( + // (item) => item.translations.filter((item) => item.lang?.slug === lang), + // ); return ( <> @@ -37,66 +110,20 @@ export default async function Page({ {about_page?.introduction}
-

- {about_page?.introduction_description_headText} -

-
-
-
    + {getdata.data.translations?.length > 0 ? (
    -
+ ) : ( + "" + )}
- - {/*
-
-
-
-
- - -
-
- - -
-
- - -
-
- -
-
-
-
-
-
-
-
*/}
@@ -114,79 +141,43 @@ export default async function Page({

-
-
- -
-
-

- {about_page?.dr_jafarian?.name} -

-
- - {about_page?.dr_jafarian?.expertise} - -
-
- - {about_page?.dr_jafarian?.position} - -
-
-
-
-
- -
-
-

- {about_page?.dr_motamedi?.name} -

-
- - {about_page?.dr_motamedi?.expertise} - -
-
- - {about_page?.dr_motamedi?.position} - -
-
-
- -
-
- -
-
-

- {about_page?.heidarnejad?.name} -

-
- - {about_page?.heidarnejad?.expertise} - -
-
- - {about_page?.heidarnejad?.position} - -
-
-
+ {getmembers?.data?.length > 0 + ? getmembers.data.map((item, index) => { + const filteredDepartmentMembersTranslations = + item.translations.find((item) => item.lang?.slug === lang); + return ( +
+
+ +
+
+

+ {filteredDepartmentMembersTranslations?.firstName}{" "} + {filteredDepartmentMembersTranslations?.lastName} +

+
+ + {filteredDepartmentMembersTranslations?.excerpt} + +
+
+ + {filteredDepartmentMembersTranslations?.position} + +
+
+
+ ); + }) + : ""}
diff --git a/src/app/[lang]/patient-rights-charter/page.tsx b/src/app/[lang]/patient-rights-charter/page.tsx index d2fdaa4..749e63e 100644 --- a/src/app/[lang]/patient-rights-charter/page.tsx +++ b/src/app/[lang]/patient-rights-charter/page.tsx @@ -1,20 +1,59 @@ -import {languages_types} from "@/types"; +import { languages_types } from "@/types"; import PageHeaderSlider from "@/ui/page-header-slider/PageHeaderSlider"; import React from "react"; -import {getDictionary} from "../dictionaries"; +import { getDictionary } from "../dictionaries"; import { pages_titles } from "@/constants"; import { Metadata } from "next"; export const metadata: Metadata = { - title: pages_titles.patient_rights_charter['fa'] + ' | ' +'بیمارستان شمال', + title: pages_titles.patient_rights_charter["fa"] + " | " + "بیمارستان شمال", description: "Shomal Hospital IPD patient rights charter page", }; + +interface patientRightsResponseType { + status: number; + data: { + translations: { + patientsRights: string | null; + language: { + title: string; + slug: string; + }; + }[]; + }; + message: string; +} + +async function getData(): Promise { + const res = await fetch( + `http://localhost:3500/api/v1/public-apis/get/human-rights`, + { + cache: "no-cache", + }, + ); + + if (!res.ok) { + throw new Error("Error"); + } + + const data = await res.json(); + + return data; +} + export default async function PatientRights({ params, }: { - params: Promise<{lang: languages_types}>; + params: Promise<{ lang: languages_types }>; }) { - const {lang} = await params; - const {patient_rights_charter} = await getDictionary(lang); + const { lang } = await params; + const { patient_rights_charter } = await getDictionary(lang); + const getdata = await getData(); + + const filteredData = + getdata.data.translations.length > 0 + ? getdata?.data?.translations.find((item) => item.language.slug === lang) + : null; + return ( <>
-

- {patient_rights_charter?.section_one?.title} -

-
-
-
- -

- {patient_rights_charter?.section_two?.title} -

-
-

- {patient_rights_charter?.section_three?.title} -

-
-

- {patient_rights_charter?.section_four?.title} -

- -
+ {filteredData ? ( +
+ ) : ( + "" + )}
diff --git a/src/app/[lang]/pricing/page.tsx b/src/app/[lang]/pricing/page.tsx index 57b1e5a..fc2741a 100644 --- a/src/app/[lang]/pricing/page.tsx +++ b/src/app/[lang]/pricing/page.tsx @@ -1,20 +1,81 @@ import PageHeaderSlider from "@/ui/page-header-slider/PageHeaderSlider"; import React from "react"; -import {getDictionary} from "../dictionaries"; -import {languages_types} from "@/types"; -import {Metadata} from "next"; -import {pages_titles} from "@/constants"; +import { getDictionary } from "../dictionaries"; +import { languages_types } from "@/types"; +import { Metadata } from "next"; +import { pages_titles } from "@/constants"; +import { toPersianNumber } from "@/utils/functions"; export const metadata: Metadata = { title: pages_titles.pricing["fa"] + " | " + "بیمارستان شمال", description: "Shomal Hospital IPD Pricing page", }; + +interface medicalPackagesDataType { + id: number; + translations: ( + | { + title: string; + lang: { + title: string; + slug: string; + } | null; + } + | { + title: string; + lang: { + title: string; + slug: string; + } | null; + } + )[]; + icon: string | null; + priority: number | null; + price: string; + parent_id: number | null; + parent: { + translations: { + title: string; + lang: { + title: string; + slug: string; + } | null; + }[]; + } | null; + children: medicalPackagesDataType[]; +} + +interface medicalPackagesResponseType { + status: number; + data: medicalPackagesDataType[]; + message: string; +} + +async function getData(lang: string): Promise { + const res = await fetch( + `http://localhost:3500/api/v1/public-apis/get/pricing/${lang ?? ""}`, + { + cache: "no-cache", + }, + ); + + if (!res.ok) { + throw new Error("Error"); + } + + const data = await res.json(); + + return data; +} export default async function PricingPage({ params, }: { - params: Promise<{lang: languages_types}>; + params: Promise<{ lang: languages_types }>; }) { - const {lang} = await params; - const {pricing_page, medical_packages} = await getDictionary(lang); + const { lang } = await params; + const { pricing_page } = await getDictionary(lang); + + const getdata = await getData(lang); + return ( <> - - - {medical_packages[0].package_name} - - - {medical_packages[0].services[0].service_name} - - 350 – 600 - - - - {medical_packages[0].services[1].service_name} - - 450 – 800 - - - - {medical_packages[0].services[2].service_name} - - 750 – 1,200 - + {getdata.data.flatMap((item) => + item.children.length > 0 ? ( + item.children.map((item2, index) => ( + + {index + 1 === 1 && ( + + { + item2.parent?.translations.find( + (translate) => translate.lang?.slug === lang, + )?.title + } + + )} + + { + item2.translations.find( + (p) => p.lang?.slug === lang, + )?.title + } + + + {lang === "fa" || lang === "ar" + ? toPersianNumber(item2.price) + : item2.price} + + + )) + ) : ( + + + { + item.translations.find( + (translate) => translate.lang?.slug === lang, + )?.title + } + - - - {medical_packages[1].package_name} - - - {medical_packages[1].services[0].service_name} - - 3,800 – 6,000 - - - - {medical_packages[1].services[1].service_name} - - 4,500 – 7,000 - - - - - {medical_packages[2].package_name} - - - {medical_packages[2].services[0].service_name} - - 900 – 1,300 - - - - {medical_packages[2].services[1].service_name} - - 3,200 – 4,200 - - - - {medical_packages[2].services[2].service_name} - - 1,500 – 2,000 - - - - {medical_packages[2].services[3].service_name} - - 3,000 – 6,000 - - - - {medical_packages[2].services[4].service_name} - - 9,000 – 12,000 - - - - {medical_packages[2].services[5].service_name} - - 7,000 – 10,000 - - - - {medical_packages[2].services[6].service_name} - - 550 – 4,500 - - - - {medical_packages[2].services[7].service_name} - - 5,500-9,000 - - - - - {medical_packages[3].package_name} - - - {medical_packages[3].services[0].service_name} - - 1,800 – 2,800 - - - - {medical_packages[3].services[1].service_name} - - 2,500 – 3,500 - - - - {medical_packages[3].services[2].service_name} - - 1,000 – 1,600 - - - - {medical_packages[3].services[3].service_name} - - 3,500 – 5,000 - - - - {medical_packages[3].services[4].service_name} - - - 2,800 – 3,800 - - - - {medical_packages[3].services[5].service_name} - - - 2,500 – 3,200 - - - - {medical_packages[3].services[6].service_name} - - 1,000 – 1,500 - - - - {medical_packages[3].services[7].service_name} - - 4,500 - 7,000 - - - - {medical_packages[3].services[8].service_name} - - 800 – 1,200 - - - - - {medical_packages[4].package_name} - - - {medical_packages[4].services[0].service_name} - - 6,600 - 11,700 - - - - {medical_packages[4].services[1].service_name} - - 5,100 - 9,000 - - - - - {medical_packages[5].package_name} - - - - - - {medical_packages[5].services[2].service_name} - - 4,500 - 9,000 - - - - - - {medical_packages[5].services[5].service_name} - - 10,000 - 35,000 - - - - - {medical_packages[6].package_name} - - - {medical_packages[6].services[0].service_name} - - - 1,200 – 1,800 - - - - {medical_packages[6].services[1].service_name} - - 1,000 – 1,500 - - - - {medical_packages[6].services[2].service_name} - - 1,000 – 1,400 - - - - {medical_packages[6].services[3].service_name} - - 1,500 – 2,500 - + + + {lang === "fa" || lang === "ar" + ? toPersianNumber(item.price) + : item.price} + + + ), + )}
diff --git a/src/app/globals.css b/src/app/globals.css index 6b6e22e..73b3ef2 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -129,3 +129,8 @@ td { p { @apply leading-relaxed my-4; } + + +.textResult ul,.textResult ol{ + @apply list-disc list-inside marker:text-secondary marker:text-2xl space-y-6 text-[#454547]; +} \ No newline at end of file diff --git a/src/constants/index.ts b/src/constants/index.ts index c5666b3..819fcb4 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,5 +1,4 @@ import {contact_us_form_types} from "@/types"; - export const site_languages = [ { label: "English", diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..e8e431f --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1,17 @@ +import { getDefaultInfo, getLanguages } from "@/services/requests"; +import { useQuery } from "@tanstack/react-query"; + + +export const useGetLanguages = () => useQuery({ + queryKey:['get-languages'], + queryFn:getLanguages, + retry:false, + refetchOnWindowFocus:false +}) + +export const useGetDefaultInfo = () =>useQuery({ + queryKey:['get-default-info'], + queryFn:getDefaultInfo, + retry:false, + refetchOnWindowFocus:false, +}) \ No newline at end of file diff --git a/src/middleware.js b/src/middleware.js index 93a0ec3..72b79fa 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -1,32 +1,31 @@ -import {NextResponse} from "next/server"; +import { NextResponse } from "next/server"; -const locales = ["en", "fa","ar"]; +const DEFAULT_LOCALE = "fa"; // fallback امن export function middleware(request) { - const {pathname} = request.nextUrl; + const { pathname } = request.nextUrl; if ( pathname.startsWith("/_next") || - pathname.startsWith("/favicon.ico") || - pathname.startsWith("/robots.txt") || - pathname.match(/^\/.*\.(png|jpg|jpeg|gif|svg|webp|ico)$/) + pathname.startsWith("/api") || + pathname.match(/\.(.*)$/) ) { return NextResponse.next(); } - const pathnameHasLocale = locales.some( - (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}` - ); - if (pathnameHasLocale) return NextResponse.next(); + const segments = pathname.split("/"); + const maybeLang = segments[1]; - const locale = "fa"; - const url = request.nextUrl.clone(); - url.pathname = `/${locale}${pathname}`; - return NextResponse.redirect(url); + // فقط چک کن آیا prefix داره یا نه + if (!maybeLang) { + return NextResponse.redirect( + new URL(`/${DEFAULT_LOCALE}${pathname}`, request.url) + ); + } + + return NextResponse.next(); } -// export const config = { -// matcher: [ -// "/((?!_next|favicon.ico|robots.txt|.*\\.(png|jpg|jpeg|gif|svg|webp|ico)).*)", -// ], -// }; +export const config = { + matcher: ["/((?!_next|.*\\..*).*)"], +}; \ No newline at end of file diff --git a/src/services/api/call-api/index.ts b/src/services/api/call-api/index.ts new file mode 100644 index 0000000..dcd50a5 --- /dev/null +++ b/src/services/api/call-api/index.ts @@ -0,0 +1,25 @@ +import axios from "axios"; + +const publicApiInstance = axios.create({ + baseURL: 'http://localhost:3500/api/v1', + withCredentials: true, +}); + +publicApiInstance.interceptors.request.use( + (res) => res, + (err) => Promise.reject(err) +); + +publicApiInstance.interceptors.response.use( + (res) => res, + async (err) => Promise.reject(err) +); + +const publicApi = { + post: publicApiInstance.post, + get: publicApiInstance.get, + put: publicApiInstance.put, + delete: publicApiInstance.delete, +}; + +export default publicApi; diff --git a/src/services/api/cdn-api/index.ts b/src/services/api/cdn-api/index.ts new file mode 100644 index 0000000..0910e45 --- /dev/null +++ b/src/services/api/cdn-api/index.ts @@ -0,0 +1,28 @@ +import axios from "axios"; + +const CDNCallAxios = axios.create({ + baseURL: "http://localhost:4000", + withCredentials: true, +}); + +CDNCallAxios.interceptors.request.use( + (config) => { + return config; + }, + (error) => Promise.reject(error), +); + +CDNCallAxios.interceptors.response.use( + (res) => res, + (error) => Promise.reject(error), +); + +const CDNCall = { + get: CDNCallAxios.get, + post: CDNCallAxios.post, + put: CDNCallAxios.put, + patch: CDNCallAxios.patch, + delete: CDNCallAxios.delete, +}; + +export default CDNCall; diff --git a/src/services/requests/index.ts b/src/services/requests/index.ts new file mode 100644 index 0000000..6c4295c --- /dev/null +++ b/src/services/requests/index.ts @@ -0,0 +1,8 @@ +import publicApi from "../api/call-api"; + +export async function getLanguages() { + return await publicApi.get("/language/get/all").then((res) => res.data); +} +export async function getDefaultInfo() { + return await publicApi.get("/default-info/get/all").then((res) => res.data); +} diff --git a/src/ui/components/LanguageContext.ts b/src/ui/components/LanguageContext.ts new file mode 100644 index 0000000..21476d2 --- /dev/null +++ b/src/ui/components/LanguageContext.ts @@ -0,0 +1,20 @@ +"use client"; + +import { createContext, useContext } from "react"; + +type Language = { + title: string; + slug: string; +}; + +export const LanguageContext = createContext(null); + +export const useLanguages = () => { + const context = useContext(LanguageContext); + + if (!context) { + throw new Error("useLanguages must be used inside LanguageProvider"); + } + + return context; +}; diff --git a/src/ui/components/LanguageProvider.tsx b/src/ui/components/LanguageProvider.tsx new file mode 100644 index 0000000..8774c83 --- /dev/null +++ b/src/ui/components/LanguageProvider.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { LanguageContext } from "./LanguageContext"; +type Language = { + title: string; + slug: string; +}; +export default function LanguageProvider({ + children, + languages, +}: { + children: React.ReactNode; + languages: Language[]; +}) { + return ( + + {children} + + ); +} diff --git a/src/ui/components/LanguageSwitcher.tsx b/src/ui/components/LanguageSwitcher.tsx new file mode 100644 index 0000000..8b10401 --- /dev/null +++ b/src/ui/components/LanguageSwitcher.tsx @@ -0,0 +1,54 @@ +"use client"; + +import { usePathname, useRouter } from "next/navigation"; +import { useLanguages } from "./LanguageContext"; +import React from "react"; + +export default function LanguageSwitcher() { + const languages = useLanguages(); + const pathname = usePathname(); + const router = useRouter(); + + const removeLangPrefix = (path: string) => { + const segments = path.split("/"); + segments.splice(1, 1); + return segments.join("/") || "/"; + }; + + const handleChangeLanguage = (slug: string) => { + const cleanPath = removeLangPrefix(pathname); + document.cookie = `NEXT_LOCALE=${slug}; path=/`; + router.push(`/${slug}${cleanPath}`); + }; + + return ( +
+ {languages.map((lang) => ( + + + + + | + + ))} + + + +
+ ); +} diff --git a/src/ui/components/LanguagesChanger.tsx b/src/ui/components/LanguagesChanger.tsx index 7369c76..37ca4ab 100644 --- a/src/ui/components/LanguagesChanger.tsx +++ b/src/ui/components/LanguagesChanger.tsx @@ -3,7 +3,7 @@ import {languages_types} from "@/types"; import {usePathname, useRouter} from "next/navigation"; import React from "react"; -export default function LanguagesChanger({lang}: {lang: languages_types}) { +export default function LanguagesChanger() { const pathname = usePathname(); const router = useRouter(); const removeLangPrefix = (path: string) => { diff --git a/src/ui/components/TopNavbarEmail.tsx b/src/ui/components/TopNavbarEmail.tsx new file mode 100644 index 0000000..6fa806b --- /dev/null +++ b/src/ui/components/TopNavbarEmail.tsx @@ -0,0 +1,14 @@ +import React from "react"; + + + +export default async function TopNavbarEmail({email}:{email:string}) { + + return ( + <> + + {email} + + + ); +} diff --git a/src/ui/components/menu/MobileMenu.tsx b/src/ui/components/menu/MobileMenu.tsx index 0caff00..b5de8c4 100644 --- a/src/ui/components/menu/MobileMenu.tsx +++ b/src/ui/components/menu/MobileMenu.tsx @@ -129,7 +129,7 @@ export default function MobileMenu({lang}: {lang: languages_types}) { )}
))} - +
diff --git a/src/ui/forms/DoctorsFilterBox.tsx b/src/ui/forms/DoctorsFilterBox.tsx index 5d1a580..1b60a40 100644 --- a/src/ui/forms/DoctorsFilterBox.tsx +++ b/src/ui/forms/DoctorsFilterBox.tsx @@ -20,7 +20,7 @@ export default function DoctorsFilterBox({ select_this: string; }; }) { - const [name, setName] = useState(defaultName); + const [name] = useState(defaultName); const [expertise, setExpertise] = useState(defaultExpertise); const router = useRouter(); diff --git a/src/ui/top-navbar/index.tsx b/src/ui/top-navbar/index.tsx index ed84da8..1c088f8 100644 --- a/src/ui/top-navbar/index.tsx +++ b/src/ui/top-navbar/index.tsx @@ -1,9 +1,28 @@ import React from "react"; -import LanguagesChanger from "../components/LanguagesChanger"; -import {languages_types} from "@/types"; -import {default_info} from "@/constants"; +import { languages_types } from "@/types"; +import TopNavbarEmail from "../components/TopNavbarEmail"; +import LanguageSwitcher from "../components/LanguageSwitcher"; +async function getDefaultInfoData() { + const res = await fetch(`${process.env.API_URL}/public-apis/top-navbar`, { + next: { revalidate: 60 }, + }); -export default function TopNavbar({lang}: {lang: languages_types}) { + if (!res.ok && res.status === 500) { + throw new Error("Fail to get data"); + } + + if (!res.ok && res.status === 404) { + return null; + } + + const data = await res.json(); + + return data; +} +export default async function TopNavbar({ lang }: { lang: languages_types }) { + const getdata = await getDefaultInfoData(); + + const { data } = getdata; return (
@@ -22,9 +41,7 @@ export default function TopNavbar({lang}: {lang: languages_types}) { /> - - {default_info.email} - +