From c1be1893f5432789d5af22738b0d853e7287208d Mon Sep 17 00:00:00 2001 From: leeheejin Date: Mon, 15 Dec 2025 16:45:26 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EC=A7=80=EA=B8=88=EC=9D=80=20=EC=B6=9C?= =?UTF-8?q?=EB=B0=9C=EC=A7=80=EB=AA=A9=EC=A0=81=EC=A7=80=EA=B0=80=20?= =?UTF-8?q?=EB=8B=AC=EB=9D=BC=EB=8F=84=20=EA=B0=95=EC=A0=9C=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=9D=B4=20=EA=B0=80=EB=8A=A5=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/lib/utils/buttonActions.ts | 64 +++++++++++++++-------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index c039d55a..4d5915e9 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -4116,39 +4116,43 @@ export class ButtonActionExecutor { try { console.log("๐Ÿ›‘ [handleTrackingStop] ์œ„์น˜ ์ถ”์  ์ข…๋ฃŒ:", { config, context }); - // ์ถ”์  ์ค‘์ธ์ง€ ํ™•์ธ - if (!this.trackingIntervalId) { - toast.warning("์ง„ํ–‰ ์ค‘์ธ ์œ„์น˜ ์ถ”์ ์ด ์—†์Šต๋‹ˆ๋‹ค."); - return false; + // ์ถ”์  ์ค‘์ธ์ง€ ํ™•์ธ (์ƒˆ๋กœ๊ณ ์นจ ํ›„์—๋„ DB ์ƒํƒœ ๊ธฐ๋ฐ˜ ์ข…๋ฃŒ ๊ฐ€๋Šฅํ•˜๋„๋ก ์ˆ˜์ •) + const isTrackingActive = !!this.trackingIntervalId; + + if (!isTrackingActive) { + // ์ถ”์  ์ค‘์ด ์•„๋‹ˆ์–ด๋„ DB ์ƒํƒœ ๋ณ€๊ฒฝ์€ ์ง„ํ–‰ (์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์ข…๋ฃŒ ์ง€์›) + console.log("โš ๏ธ [handleTrackingStop] trackingIntervalId ์—†์Œ - DB ์ƒํƒœ ๊ธฐ๋ฐ˜ ์ข…๋ฃŒ ์ง„ํ–‰"); + } else { + // ํƒ€์ด๋จธ ์ •๋ฆฌ (์ถ”์  ์ค‘์ธ ๊ฒฝ์šฐ์—๋งŒ) + clearInterval(this.trackingIntervalId); + this.trackingIntervalId = null; } - // ํƒ€์ด๋จธ ์ •๋ฆฌ - clearInterval(this.trackingIntervalId); - this.trackingIntervalId = null; - const tripId = this.currentTripId; - // ๋งˆ์ง€๋ง‰ ์œ„์น˜ ์ €์žฅ (trip_status๋ฅผ completed๋กœ) - const departure = - this.trackingContext?.formData?.[this.trackingConfig?.trackingDepartureField || "departure"] || null; - const arrival = this.trackingContext?.formData?.[this.trackingConfig?.trackingArrivalField || "arrival"] || null; - const departureName = this.trackingContext?.formData?.["departure_name"] || null; - const destinationName = this.trackingContext?.formData?.["destination_name"] || null; - const vehicleId = - this.trackingContext?.formData?.[this.trackingConfig?.trackingVehicleIdField || "vehicle_id"] || null; + // ๋งˆ์ง€๋ง‰ ์œ„์น˜ ์ €์žฅ (์ถ”์  ์ค‘์ด์—ˆ๋˜ ๊ฒฝ์šฐ์—๋งŒ) + if (isTrackingActive) { + const departure = + this.trackingContext?.formData?.[this.trackingConfig?.trackingDepartureField || "departure"] || null; + const arrival = this.trackingContext?.formData?.[this.trackingConfig?.trackingArrivalField || "arrival"] || null; + const departureName = this.trackingContext?.formData?.["departure_name"] || null; + const destinationName = this.trackingContext?.formData?.["destination_name"] || null; + const vehicleId = + this.trackingContext?.formData?.[this.trackingConfig?.trackingVehicleIdField || "vehicle_id"] || null; - await this.saveLocationToHistory( - tripId, - departure, - arrival, - departureName, - destinationName, - vehicleId, - "completed", - ); + await this.saveLocationToHistory( + tripId, + departure, + arrival, + departureName, + destinationName, + vehicleId, + "completed", + ); + } - // ๐Ÿ†• ๊ฑฐ๋ฆฌ/์‹œ๊ฐ„ ๊ณ„์‚ฐ ๋ฐ ์ €์žฅ - if (tripId) { + // ๐Ÿ†• ๊ฑฐ๋ฆฌ/์‹œ๊ฐ„ ๊ณ„์‚ฐ ๋ฐ ์ €์žฅ (์ถ”์  ์ค‘์ด์—ˆ๋˜ ๊ฒฝ์šฐ์—๋งŒ) + if (isTrackingActive && tripId) { try { const tripStats = await this.calculateTripStats(tripId); console.log("๐Ÿ“Š ์šดํ–‰ ํ†ต๊ณ„:", tripStats); @@ -4260,9 +4264,9 @@ export class ButtonActionExecutor { } } - // ์ƒํƒœ ๋ณ€๊ฒฝ (vehicles ํ…Œ์ด๋ธ” ๋“ฑ) - const effectiveConfig = config.trackingStatusOnStop ? config : this.trackingConfig; - const effectiveContext = context.userId ? context : this.trackingContext; + // ์ƒํƒœ ๋ณ€๊ฒฝ (vehicles ํ…Œ์ด๋ธ” ๋“ฑ) - ์ƒˆ๋กœ๊ณ ์นจ ํ›„์—๋„ ๋™์ž‘ํ•˜๋„๋ก config ์šฐ์„  ์‚ฌ์šฉ + const effectiveConfig = config.trackingStatusOnStop ? config : this.trackingConfig || config; + const effectiveContext = context.userId ? context : this.trackingContext || context; if (effectiveConfig?.trackingStatusOnStop && effectiveConfig?.trackingStatusField && effectiveContext) { try { -- 2.43.0 From 7f15861b6efdc4be791e9468a479b687dcdd3b0d Mon Sep 17 00:00:00 2001 From: leeheejin Date: Mon, 15 Dec 2025 16:54:03 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EC=B6=9C=EB=B0=9C=EC=A7=80=EB=8F=84?= =?UTF-8?q?=EC=B0=A9=EC=A7=80=20=EB=94=94=EB=B9=84=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=81=8C=EC=96=B4=EC=98=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LocationSwapSelectorComponent.tsx | 99 ++++++++++++++++++- .../LocationSwapSelectorConfigPanel.tsx | 54 ++++++++++ frontend/lib/utils/buttonActions.ts | 43 +++++++- 3 files changed, 191 insertions(+), 5 deletions(-) diff --git a/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorComponent.tsx b/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorComponent.tsx index 3f1a723b..7a693ad5 100644 --- a/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorComponent.tsx +++ b/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorComponent.tsx @@ -53,6 +53,9 @@ export interface LocationSwapSelectorProps { formData?: Record; onFormDataChange?: (field: string, value: any) => void; + // ๐Ÿ†• ์‚ฌ์šฉ์ž ์ •๋ณด (DB์—์„œ ์ดˆ๊ธฐ๊ฐ’ ๋กœ๋“œ์šฉ) + userId?: string; + // componentConfig (ํ™”๋ฉด ๋””์ž์ด๋„ˆ์—์„œ ์ „๋‹ฌ) componentConfig?: { dataSource?: DataSourceConfig; @@ -65,6 +68,10 @@ export interface LocationSwapSelectorProps { showSwapButton?: boolean; swapButtonPosition?: "center" | "right"; variant?: "card" | "inline" | "minimal"; + // ๐Ÿ†• DB ์ดˆ๊ธฐ๊ฐ’ ๋กœ๋“œ ์„ค์ • + loadFromDb?: boolean; // DB์—์„œ ์ดˆ๊ธฐ๊ฐ’ ๋กœ๋“œ ์—ฌ๋ถ€ + dbTableName?: string; // ์กฐํšŒํ•  ํ…Œ์ด๋ธ”๋ช… (๊ธฐ๋ณธ: vehicles) + dbKeyField?: string; // ํ‚ค ํ•„๋“œ (๊ธฐ๋ณธ: user_id) }; } @@ -80,6 +87,7 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) formData = {}, onFormDataChange, componentConfig, + userId, } = props; // componentConfig์—์„œ ์„ค์ • ๊ฐ€์ ธ์˜ค๊ธฐ (์šฐ์„ ์ˆœ์œ„: componentConfig > props) @@ -93,6 +101,11 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) const destinationLabel = config.destinationLabel || props.destinationLabel || "๋„์ฐฉ์ง€"; const showSwapButton = config.showSwapButton !== false && props.showSwapButton !== false; const variant = config.variant || props.variant || "card"; + + // ๐Ÿ†• DB ์ดˆ๊ธฐ๊ฐ’ ๋กœ๋“œ ์„ค์ • + const loadFromDb = config.loadFromDb !== false; // ๊ธฐ๋ณธ๊ฐ’ true + const dbTableName = config.dbTableName || "vehicles"; + const dbKeyField = config.dbKeyField || "user_id"; // ๊ธฐ๋ณธ ์˜ต์…˜ (ํฌํ•ญ/๊ด‘์–‘) const DEFAULT_OPTIONS: LocationOption[] = [ @@ -104,6 +117,7 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) const [options, setOptions] = useState(DEFAULT_OPTIONS); const [loading, setLoading] = useState(false); const [isSwapping, setIsSwapping] = useState(false); + const [dbLoaded, setDbLoaded] = useState(false); // DB ๋กœ๋“œ ์™„๋ฃŒ ์—ฌ๋ถ€ // ๋กœ์ปฌ ์„ ํƒ ์ƒํƒœ (Select ์ปดํฌ๋„ŒํŠธ์šฉ) const [localDeparture, setLocalDeparture] = useState(""); @@ -193,8 +207,89 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) loadOptions(); }, [dataSource, isDesignMode]); - // formData์—์„œ ์ดˆ๊ธฐ๊ฐ’ ๋™๊ธฐํ™” + // ๐Ÿ†• DB์—์„œ ์ดˆ๊ธฐ๊ฐ’ ๋กœ๋“œ (์ƒˆ๋กœ๊ณ ์นจ ์‹œ์—๋„ ์ถœ๋ฐœ์ง€/๋ชฉ์ ์ง€ ์œ ์ง€) useEffect(() => { + const loadFromDatabase = async () => { + // ๋””์ž์ธ ๋ชจ๋“œ์ด๊ฑฐ๋‚˜, DB ๋กœ๋“œ ๋น„ํ™œ์„ฑํ™”์ด๊ฑฐ๋‚˜, userId๊ฐ€ ์—†์œผ๋ฉด ์Šคํ‚ต + if (isDesignMode || !loadFromDb || !userId) { + console.log("[LocationSwapSelector] DB ๋กœ๋“œ ์Šคํ‚ต:", { isDesignMode, loadFromDb, userId }); + return; + } + + // ์ด๋ฏธ ๋กœ๋“œํ–ˆ์œผ๋ฉด ์Šคํ‚ต + if (dbLoaded) { + return; + } + + try { + console.log("[LocationSwapSelector] DB์—์„œ ์ถœ๋ฐœ์ง€/๋ชฉ์ ์ง€ ๋กœ๋“œ ์‹œ์ž‘:", { dbTableName, dbKeyField, userId }); + + const response = await apiClient.post( + `/table-management/tables/${dbTableName}/data`, + { + page: 1, + size: 1, + search: { [dbKeyField]: userId }, + autoFilter: true, + } + ); + + const vehicleData = response.data?.data?.data?.[0] || response.data?.data?.rows?.[0]; + + if (vehicleData) { + const dbDeparture = vehicleData[departureField] || vehicleData.departure; + const dbDestination = vehicleData[destinationField] || vehicleData.arrival || vehicleData.destination; + + console.log("[LocationSwapSelector] DB์—์„œ ๋กœ๋“œ๋œ ๊ฐ’:", { dbDeparture, dbDestination }); + + // DB์— ๊ฐ’์ด ์žˆ์œผ๋ฉด ๋กœ์ปฌ ์ƒํƒœ ๋ฐ formData ์—…๋ฐ์ดํŠธ + if (dbDeparture && options.some(o => o.value === dbDeparture)) { + setLocalDeparture(dbDeparture); + onFormDataChange?.(departureField, dbDeparture); + + // ๋ผ๋ฒจ๋„ ์—…๋ฐ์ดํŠธ + if (departureLabelField) { + const opt = options.find(o => o.value === dbDeparture); + if (opt) { + onFormDataChange?.(departureLabelField, opt.label); + } + } + } + + if (dbDestination && options.some(o => o.value === dbDestination)) { + setLocalDestination(dbDestination); + onFormDataChange?.(destinationField, dbDestination); + + // ๋ผ๋ฒจ๋„ ์—…๋ฐ์ดํŠธ + if (destinationLabelField) { + const opt = options.find(o => o.value === dbDestination); + if (opt) { + onFormDataChange?.(destinationLabelField, opt.label); + } + } + } + } + + setDbLoaded(true); + } catch (error) { + console.error("[LocationSwapSelector] DB ๋กœ๋“œ ์‹คํŒจ:", error); + setDbLoaded(true); // ์‹คํŒจํ•ด๋„ ๋‹ค์‹œ ์‹œ๋„ํ•˜์ง€ ์•Š์Œ + } + }; + + // ์˜ต์…˜์ด ๋กœ๋“œ๋œ ํ›„์— DB ๋กœ๋“œ ์‹คํ–‰ + if (options.length > 0) { + loadFromDatabase(); + } + }, [userId, loadFromDb, dbTableName, dbKeyField, departureField, destinationField, options, isDesignMode, dbLoaded, onFormDataChange, departureLabelField, destinationLabelField]); + + // formData์—์„œ ์ดˆ๊ธฐ๊ฐ’ ๋™๊ธฐํ™” (DB ๋กœ๋“œ ํ›„์—๋„ formData ๋ณ€๊ฒฝ ์‹œ ๋ฐ˜์˜) + useEffect(() => { + // DB ๋กœ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์œผ๋ฉด ์Šคํ‚ต (DB ๊ฐ’ ์šฐ์„ ) + if (loadFromDb && userId && !dbLoaded) { + return; + } + const depVal = formData[departureField]; const destVal = formData[destinationField]; @@ -204,7 +299,7 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) if (destVal && options.some(o => o.value === destVal)) { setLocalDestination(destVal); } - }, [formData, departureField, destinationField, options]); + }, [formData, departureField, destinationField, options, loadFromDb, userId, dbLoaded]); // ์ถœ๋ฐœ์ง€ ๋ณ€๊ฒฝ const handleDepartureChange = (selectedValue: string) => { diff --git a/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorConfigPanel.tsx b/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorConfigPanel.tsx index 4e21cddf..cd84806f 100644 --- a/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorConfigPanel.tsx +++ b/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorConfigPanel.tsx @@ -470,6 +470,58 @@ export function LocationSwapSelectorConfigPanel({ + {/* DB ์ดˆ๊ธฐ๊ฐ’ ๋กœ๋“œ ์„ค์ • */} +
+

DB ์ดˆ๊ธฐ๊ฐ’ ๋กœ๋“œ

+

+ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ์—๋„ DB์— ์ €์žฅ๋œ ์ถœ๋ฐœ์ง€/๋ชฉ์ ์ง€๋ฅผ ์ž๋™์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค +

+ +
+ + handleChange("loadFromDb", checked)} + /> +
+ + {config?.loadFromDb !== false && ( + <> +
+ + +
+ +
+ + handleChange("dbKeyField", e.target.value)} + placeholder="user_id" + /> +

+ ํ˜„์žฌ ์‚ฌ์šฉ์ž ID๋กœ ์กฐํšŒํ•  ํ•„๋“œ (๊ธฐ๋ณธ: user_id) +

+
+ + )} +
+ {/* ์•ˆ๋‚ด */}

@@ -480,6 +532,8 @@ export function LocationSwapSelectorConfigPanel({ 2. ์ถœ๋ฐœ์ง€/๋„์ฐฉ์ง€ ๊ฐ’์ด ์ €์žฅ๋  ํ•„๋“œ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค
3. ๊ตํ™˜ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ถœ๋ฐœ์ง€์™€ ๋„์ฐฉ์ง€๊ฐ€ ๋ฐ”๋€๋‹ˆ๋‹ค +
+ 4. DB ์ดˆ๊ธฐ๊ฐ’ ๋กœ๋“œ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉด ์ƒˆ๋กœ๊ณ ์นจ ํ›„์—๋„ ๊ฐ’์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค

diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 4d5915e9..e1573998 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -4130,14 +4130,51 @@ export class ButtonActionExecutor { const tripId = this.currentTripId; + // ๐Ÿ†• DB์—์„œ ์ถœ๋ฐœ์ง€/๋ชฉ์ ์ง€ ์กฐํšŒ (์šด์ „์ž๊ฐ€ ์ค‘๊ฐ„์— ๋ฐ”๊ฟ”๋„ ์›๋ž˜ ๊ฐ’ ์‚ฌ์šฉ) + let dbDeparture: string | null = null; + let dbArrival: string | null = null; + let dbVehicleId: string | null = null; + + const userId = context.userId || this.trackingUserId; + if (userId) { + try { + const { apiClient } = await import("@/lib/api/client"); + const statusTableName = config.trackingStatusTableName || this.trackingConfig?.trackingStatusTableName || context.tableName || "vehicles"; + const keyField = config.trackingStatusKeyField || this.trackingConfig?.trackingStatusKeyField || "user_id"; + + // DB์—์„œ ํ˜„์žฌ ์ฐจ๋Ÿ‰ ์ •๋ณด ์กฐํšŒ + const vehicleResponse = await apiClient.post( + `/table-management/tables/${statusTableName}/data`, + { + page: 1, + size: 1, + search: { [keyField]: userId }, + autoFilter: true, + }, + ); + + const vehicleData = vehicleResponse.data?.data?.data?.[0] || vehicleResponse.data?.data?.rows?.[0]; + if (vehicleData) { + dbDeparture = vehicleData.departure || null; + dbArrival = vehicleData.arrival || null; + dbVehicleId = vehicleData.id || vehicleData.vehicle_id || null; + console.log("๐Ÿ“ [handleTrackingStop] DB์—์„œ ์ถœ๋ฐœ์ง€/๋ชฉ์ ์ง€ ์กฐํšŒ:", { dbDeparture, dbArrival, dbVehicleId }); + } + } catch (dbError) { + console.warn("โš ๏ธ [handleTrackingStop] DB ์กฐํšŒ ์‹คํŒจ, formData ์‚ฌ์šฉ:", dbError); + } + } + // ๋งˆ์ง€๋ง‰ ์œ„์น˜ ์ €์žฅ (์ถ”์  ์ค‘์ด์—ˆ๋˜ ๊ฒฝ์šฐ์—๋งŒ) if (isTrackingActive) { - const departure = + // DB ๊ฐ’ ์šฐ์„ , ์—†์œผ๋ฉด formData ์‚ฌ์šฉ + const departure = dbDeparture || this.trackingContext?.formData?.[this.trackingConfig?.trackingDepartureField || "departure"] || null; - const arrival = this.trackingContext?.formData?.[this.trackingConfig?.trackingArrivalField || "arrival"] || null; + const arrival = dbArrival || + this.trackingContext?.formData?.[this.trackingConfig?.trackingArrivalField || "arrival"] || null; const departureName = this.trackingContext?.formData?.["departure_name"] || null; const destinationName = this.trackingContext?.formData?.["destination_name"] || null; - const vehicleId = + const vehicleId = dbVehicleId || this.trackingContext?.formData?.[this.trackingConfig?.trackingVehicleIdField || "vehicle_id"] || null; await this.saveLocationToHistory( -- 2.43.0