update
This commit is contained in:
Generated
+12
-5
@@ -3,9 +3,8 @@
|
|||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="bb7b50a5-be9c-4cd7-a05b-979f0e91b173" name="Changes" comment="update">
|
<list default="true" id="bb7b50a5-be9c-4cd7-a05b-979f0e91b173" name="Changes" comment="update">
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/components/CartDrawer.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/CartDrawer.jsx" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/components/MapSection.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/MapSection.jsx" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/components/ChatWidget.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/ChatWidget.jsx" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/public/images/20260803_052807.png" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/components/TicketForm.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/components/TicketForm.jsx" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -88,7 +87,7 @@
|
|||||||
<workItem from="1785583517424" duration="145000" />
|
<workItem from="1785583517424" duration="145000" />
|
||||||
<workItem from="1785645096071" duration="26000" />
|
<workItem from="1785645096071" duration="26000" />
|
||||||
<workItem from="1785646314677" duration="16621000" />
|
<workItem from="1785646314677" duration="16621000" />
|
||||||
<workItem from="1785730130350" duration="9270000" />
|
<workItem from="1785730130350" duration="11161000" />
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00001" summary="update">
|
<task id="LOCAL-00001" summary="update">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
@@ -186,7 +185,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1785745317460</updated>
|
<updated>1785745317460</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="13" />
|
<task id="LOCAL-00013" summary="update">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1785745546500</created>
|
||||||
|
<option name="number" value="00013" />
|
||||||
|
<option name="presentableId" value="LOCAL-00013" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1785745546500</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="14" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
|||||||
+96
-82
@@ -3,11 +3,12 @@
|
|||||||
// نقشه نشان (Neshan) — از Static Map API استفاده میکند. تا وقتی کلید API خالی باشد،
|
// نقشه نشان (Neshan) — از Static Map API استفاده میکند. تا وقتی کلید API خالی باشد،
|
||||||
// بهجای نقشه یک پیام راهنما نشان داده میشود (نه تصویر خراب).
|
// بهجای نقشه یک پیام راهنما نشان داده میشود (نه تصویر خراب).
|
||||||
|
|
||||||
import { useState } from 'react';
|
import {useState} from 'react';
|
||||||
import { EnvironmentOutlined } from '@ant-design/icons';
|
import {EnvironmentOutlined} from '@ant-design/icons';
|
||||||
import { useLanguage } from '@/app/context/LanguageContext';
|
import {useLanguage} from '@/app/context/LanguageContext';
|
||||||
import { useTheme } from '@/app/context/ThemeContext';
|
import {useTheme} from '@/app/context/ThemeContext';
|
||||||
import { mapLocations } from '@/data/content';
|
import {mapLocations} from '@/data/content';
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Neshan (نشان) map integration.
|
// Neshan (نشان) map integration.
|
||||||
@@ -23,94 +24,107 @@ const NESHAN_API_KEY = process.env.NEXT_PUBLIC_NESHAN_API_KEY || '';
|
|||||||
// Verify query param names/values against the current Neshan docs
|
// Verify query param names/values against the current Neshan docs
|
||||||
// (https://platform.neshan.org/api/static-map/) if this ever needs updating —
|
// (https://platform.neshan.org/api/static-map/) if this ever needs updating —
|
||||||
// `type` accepts their day/night map styles, e.g. "standard-day" / "standard-night".
|
// `type` accepts their day/night map styles, e.g. "standard-day" / "standard-night".
|
||||||
function staticMapUrl({ lat, lng }, mapStyle) {
|
function staticMapUrl({lat, lng}, mapStyle) {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
key: NESHAN_API_KEY,
|
key: NESHAN_API_KEY,
|
||||||
type: mapStyle,
|
type: mapStyle,
|
||||||
center: `${lat},${lng}`,
|
center: `${lat},${lng}`,
|
||||||
zoom: '15',
|
zoom: '15',
|
||||||
size: '600x400',
|
size: '600x400',
|
||||||
marker: `${lat},${lng}`,
|
marker: `${lat},${lng}`,
|
||||||
});
|
});
|
||||||
return `https://api.neshan.org/v5/static?${params.toString()}`;
|
return `https://api.neshan.org/v5/static?${params.toString()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function neshanViewUrl({ lat, lng }) {
|
function neshanViewUrl({lat, lng}) {
|
||||||
return `https://neshan.org/maps/@${lat},${lng},15z`;
|
return `https://neshan.org/maps/@${lat},${lng},15z`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function MapThumb({ coords, mapStyle, alt }) {
|
function MapThumb({coords, mapStyle, alt}) {
|
||||||
const [errored, setErrored] = useState(false);
|
const [errored, setErrored] = useState(false);
|
||||||
const { t } = useLanguage();
|
const {t} = useLanguage();
|
||||||
|
|
||||||
|
if (!NESHAN_API_KEY || errored) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="w-full h-full min-h-[280px] flex flex-col items-center justify-center gap-3 bg-surface text-secondary text-sm text-center p-6">
|
||||||
|
<EnvironmentOutlined className="text-3xl text-brand-400"/>
|
||||||
|
<p>{t.map.configNotice}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!NESHAN_API_KEY || errored) {
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full min-h-[280px] flex flex-col items-center justify-center gap-3 bg-surface text-secondary text-sm text-center p-6">
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
<EnvironmentOutlined className="text-3xl text-brand-400" />
|
<img
|
||||||
<p>{t.map.configNotice}</p>
|
src={staticMapUrl(coords, mapStyle)}
|
||||||
</div>
|
alt={alt}
|
||||||
|
onError={() => setErrored(true)}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
|
||||||
<img
|
|
||||||
src={staticMapUrl(coords, mapStyle)}
|
|
||||||
alt={alt}
|
|
||||||
onError={() => setErrored(true)}
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MapSection() {
|
export default function MapSection() {
|
||||||
const { t } = useLanguage();
|
const {t} = useLanguage();
|
||||||
const { theme } = useTheme();
|
const {theme} = useTheme();
|
||||||
const mapStyle = theme === 'dark' ? 'standard-night' : 'standard-day';
|
const mapStyle = theme === 'dark' ? 'standard-night' : 'standard-day';
|
||||||
|
|
||||||
const pins = [
|
const pins = [
|
||||||
{ key: 'factory', label: t.map.factoryLabel, coords: mapLocations.factory, address: t.contactInfo.factoryAddress },
|
{
|
||||||
{ key: 'sales', label: t.map.salesLabel, coords: mapLocations.salesOffice, address: t.contactInfo.salesOfficeAddress },
|
key: 'factory',
|
||||||
];
|
label: t.map.factoryLabel,
|
||||||
|
coords: mapLocations.factory,
|
||||||
|
address: t.contactInfo.factoryAddress
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'sales',
|
||||||
|
label: t.map.salesLabel,
|
||||||
|
coords: mapLocations.salesOffice,
|
||||||
|
address: t.contactInfo.salesOfficeAddress
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="map" className="relative px-6 md:px-16 py-20 md:py-28 bg-page border-t border-subtle">
|
<section id="map" className="relative px-6 md:px-16 py-20 md:py-28 bg-page border-t border-subtle">
|
||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12">
|
||||||
<span className="text-brand-400 text-sm font-semibold tracking-wide">{t.map.eyebrow}</span>
|
<span className="text-brand-400 text-sm font-semibold tracking-wide">{t.map.eyebrow}</span>
|
||||||
<h2 className="text-2xl md:text-4xl font-extrabold brand-gradient-text mt-3">{t.map.title}</h2>
|
<h2 className="text-2xl md:text-4xl font-extrabold brand-gradient-text mt-3">{t.map.title}</h2>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid lg:grid-cols-[1.3fr,1fr] gap-6">
|
|
||||||
<div className="rounded-2xl overflow-hidden border border-subtle h-[360px] lg:h-auto min-h-[360px]">
|
|
||||||
<MapThumb coords={pins[0].coords} mapStyle={mapStyle} alt={pins[0].label} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
{pins.map((pin) => (
|
|
||||||
<div key={pin.key} className="bg-surface border border-subtle rounded-2xl p-5">
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className="w-10 h-10 shrink-0 rounded-lg bg-brand/15 border border-subtle flex items-center justify-center text-brand-400">
|
|
||||||
<EnvironmentOutlined />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="text-primary font-bold text-sm mb-1">{pin.label}</h3>
|
|
||||||
<p className="text-secondary text-sm leading-7 mb-2">{pin.address}</p>
|
|
||||||
<a
|
|
||||||
href={neshanViewUrl(pin.coords)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-brand-400 hover:text-brand-300 text-xs font-semibold transition-colors"
|
|
||||||
>
|
|
||||||
{t.map.openInMaps} ↗
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
))}
|
<div className="grid lg:grid-cols-[1.3fr,1fr] gap-6">
|
||||||
</div>
|
<div className="rounded-2xl overflow-hidden border border-subtle h-[360px] lg:h-auto min-h-[360px]">
|
||||||
</div>
|
{/*<MapThumb coords={pins[0].coords} mapStyle={mapStyle} alt={pins[0].label} />*/}
|
||||||
</div>
|
<Image src="/images/map.png" alt="روبات مارکت" width={600} height={600} className={"object-contain"} />
|
||||||
</section>
|
</div>
|
||||||
);
|
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
{pins.map((pin) => (
|
||||||
|
<div key={pin.key} className="bg-surface border border-subtle rounded-2xl p-5">
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div
|
||||||
|
className="w-10 h-10 shrink-0 rounded-lg bg-brand/15 border border-subtle flex items-center justify-center text-brand-400">
|
||||||
|
<EnvironmentOutlined/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-primary font-bold text-sm mb-1">{pin.label}</h3>
|
||||||
|
<p className="text-secondary text-sm leading-7 mb-2">{pin.address}</p>
|
||||||
|
<a
|
||||||
|
href={neshanViewUrl(pin.coords)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-brand-400 hover:text-brand-300 text-xs font-semibold transition-colors"
|
||||||
|
>
|
||||||
|
{t.map.openInMaps} ↗
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 821 KiB |
Reference in New Issue
Block a user