Skip to content

Commit adee7b0

Browse files
committed
fixed x-y positioning of sockets on the plate canvas bug, added dynamic error handling for socket generation
1 parent f8f8cc5 commit adee7b0

4 files changed

Lines changed: 95 additions & 42 deletions

File tree

README.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,23 @@ A comprehensive, interactive German electrical plate and socket configuration to
55
- **Live-Demo:** [https://plate-socket-generator-tool.vercel.app/](https://plate-socket-generator-tool.vercel.app/)
66

77
![Screenshot 2025-11-02 at 14 26 49](https://github.com/user-attachments/assets/a6ecf70c-af51-4ab7-954c-e1b2372cd569)
8-
![Screenshot 2025-11-02 at 14 22 59](https://github.com/user-attachments/assets/4b87ce21-28f7-4544-8af3-8406ecc5eb3e)
9-
![Screenshot 2025-11-02 at 14 31 29](https://github.com/user-attachments/assets/bede4acb-839c-4bda-891f-86cdfe0998a6)
108

119
## 📋 Table of Contents
1210

13-
- [Overview](#overview)
14-
- [Features](#features)
15-
- [Technology Stack](#technology-stack)
16-
- [Project Structure](#project-structure)
17-
- [Installation & Setup](#installation--setup)
18-
- [Usage Guide](#usage-guide)
19-
- [Technical Implementation](#technical-implementation)
20-
- [Task Requirements](#task-requirements)
21-
- [Component Architecture](#component-architecture)
22-
- [Key Algorithms](#key-algorithms)
23-
- [Development](#development)
24-
- [Deployment](#deployment)
25-
- [Keywords](#keywords)
26-
- [Author Information](#author-information)
11+
- [Overview](#-overview)
12+
- [Features](#-features)
13+
- [Technology Stack](#️-technology-stack)
14+
- [Project Structure](#-project-structure)
15+
- [Installation & Setup](#-installation--setup)
16+
- [Usage Guide](#-usage-guide)
17+
- [Technical Implementation](#-technical-implementation)
18+
- [Task Requirements](#-task-requirements)
19+
- [Component Architecture](#️-component-architecture)
20+
- [Key Algorithms](#-key-algorithms)
21+
- [Development](#-development)
22+
- [Deployment](#-deployment)
23+
- [Keywords](#️-keywords)
24+
- [Author Information](#-author-information)
2725

2826
---
2927

components/plate-canvas.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,14 @@ const PlateCanvas = forwardRef<HTMLDivElement, PlateCanvasProps>(
112112

113113
// Calculate position of first socket's bottom-left corner
114114
// The anchor is at 3.5cm from left edge of first socket, so first socket left = positionX - 3.5
115-
// The anchor is at 3.5cm from bottom edge of first socket, so first socket bottom = positionY + 3.5
116-
const firstSocketBottom = socketGroup.positionY + ANCHOR_OFFSET_CM;
115+
// The anchor is at 3.5cm from bottom edge of first socket, so first socket bottom = positionY - 3.5
116+
const firstSocketBottom = socketGroup.positionY - ANCHOR_OFFSET_CM;
117117

118118
return {
119119
// 'left' property: distance from left edge to first socket's left edge
120120
x: (socketGroup.positionX - ANCHOR_OFFSET_CM) * scale,
121121
// 'bottom' property: distance from bottom edge to group's bottom edge
122-
// Group's bottom = first socket's bottom = positionY + 3.5
122+
// Group's bottom = first socket's bottom = positionY - 3.5
123123
y: firstSocketBottom * scale,
124124
};
125125
};

components/socket-generator.tsx

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ const DEFAULT_PLATES: Plate[] = [
5050
{ id: "2", width: 200, height: 100 },
5151
];
5252

53+
// Default socket group position
54+
// Task Requirement: "Socket groups must be at least 3 cm from plate edges"
55+
// Task Requirement: "The anchor point is the bottom-left center of the first socket"
56+
// Logic: Anchor point is 3.5cm inside the socket (center of 7x7cm socket)
57+
// For socket edge to be exactly 3cm from plate edge: anchor must be at 3.0 + 3.5 = 6.5cm from plate edges
58+
const DEFAULT_SOCKET_POSITION_X = MIN_EDGE_DISTANCE + 3.5; // 6.5 cm
59+
const DEFAULT_SOCKET_POSITION_Y = MIN_EDGE_DISTANCE + 3.5; // 6.5 cm
60+
5361
export default function SocketGenerator() {
5462
// Task Requirement: "The app must maintain realistic proportions between plates and sockets, support user input, and be responsive across different screen sizes including mobile."
5563
// Task Requirement: "On load, generate a default plate with predefined dimensions."
@@ -587,14 +595,14 @@ export default function SocketGenerator() {
587595
// Logic: Check if the first valid plate is found.
588596
if (firstValidPlate) {
589597
// Task Requirement: "The app must maintain realistic proportions between plates and sockets, support user input, and be responsive across different screen sizes including mobile."
590-
// Logic: Create a new socket group.
598+
// Logic: Create a new socket group with default position that ensures 3cm minimum distance from plate edges.
591599
const newSocketGroup: SocketGroup = {
592600
id: Date.now().toString(),
593601
plateId: firstValidPlate.id,
594602
count: 1,
595603
direction: "vertical",
596-
positionX: MIN_EDGE_DISTANCE,
597-
positionY: MIN_EDGE_DISTANCE,
604+
positionX: DEFAULT_SOCKET_POSITION_X,
605+
positionY: DEFAULT_SOCKET_POSITION_Y,
598606
};
599607
// Task Requirement: "The app must maintain realistic proportions between plates and sockets, support user input, and be responsive across different screen sizes including mobile."
600608
// Logic: Set the socket groups.
@@ -607,8 +615,8 @@ export default function SocketGenerator() {
607615
// Logic: Set the socket count.
608616
setSocketCount(1);
609617
setSocketDirection("vertical");
610-
setPositionX(MIN_EDGE_DISTANCE.toString());
611-
setPositionY(MIN_EDGE_DISTANCE.toString());
618+
setPositionX(DEFAULT_SOCKET_POSITION_X.toString());
619+
setPositionY(DEFAULT_SOCKET_POSITION_Y.toString());
612620
}
613621
}
614622
} else {
@@ -926,6 +934,11 @@ export default function SocketGenerator() {
926934
const deletedPlate = plates.find((p) => p.id === plateId);
927935
const plateNumber = plates.findIndex((p) => p.id === plateId) + 1;
928936

937+
// Find socket groups that belong to this plate BEFORE deletion
938+
const deletedSocketGroups = socketGroups.filter(
939+
(sg) => sg.plateId === plateId
940+
);
941+
929942
// Task Requirement: "The app must maintain realistic proportions between plates and sockets, support user input, and be responsive across different screen sizes including mobile."
930943
// Logic: Remove the plates.
931944
setPlates((prev) => prev.filter((p) => p.id !== plateId));
@@ -937,12 +950,26 @@ export default function SocketGenerator() {
937950
setSelectedSocketGroup(null);
938951
}
939952

940-
// Show success toast notification with plate details
953+
// Show success toast notification with plate details and socket group information
941954
if (deletedPlate) {
955+
let description = `Rückwand #${plateNumber}: ${deletedPlate.width.toFixed(
956+
1
957+
)} × ${deletedPlate.height.toFixed(1)} cm wurde entfernt`;
958+
959+
// If socket groups existed on this plate, include them in the toast
960+
if (deletedSocketGroups.length > 0) {
961+
const socketGroupsText = deletedSocketGroups
962+
.map((sg, idx) => {
963+
return `${idx + 1}. Rückwand - ${deletedPlate.width.toFixed(
964+
1
965+
)} × ${deletedPlate.height.toFixed(1)} | ${sg.count} × Steckdose`;
966+
})
967+
.join(", ");
968+
description += `. ${deletedSocketGroups.length} Steckdosengruppe(n) aus "Konfigurierte Steckdosen bestätigen" entfernt: ${socketGroupsText}`;
969+
}
970+
942971
toast.success("Rückwand gelöscht", {
943-
description: `Rückwand #${plateNumber}: ${deletedPlate.width.toFixed(
944-
1
945-
)} × ${deletedPlate.height.toFixed(1)} cm wurde entfernt`,
972+
description,
946973
});
947974
}
948975
} else {
@@ -1027,8 +1054,8 @@ export default function SocketGenerator() {
10271054
// Reset all configuration fields to default values
10281055
setSocketCount(1);
10291056
setSocketDirection("vertical");
1030-
setPositionX(MIN_EDGE_DISTANCE.toString());
1031-
setPositionY(MIN_EDGE_DISTANCE.toString());
1057+
setPositionX(DEFAULT_SOCKET_POSITION_X.toString());
1058+
setPositionY(DEFAULT_SOCKET_POSITION_Y.toString());
10321059
// Clear any previous error messages
10331060
setErrorMessage("");
10341061

@@ -1162,8 +1189,8 @@ export default function SocketGenerator() {
11621189
setSelectedPlate(validPlateForReset?.id || "");
11631190
setSocketCount(1);
11641191
setSocketDirection("vertical");
1165-
setPositionX(MIN_EDGE_DISTANCE.toString());
1166-
setPositionY(MIN_EDGE_DISTANCE.toString());
1192+
setPositionX(DEFAULT_SOCKET_POSITION_X.toString());
1193+
setPositionY(DEFAULT_SOCKET_POSITION_Y.toString());
11671194

11681195
// Keep isEditingSocket = true so user can continue adding more sockets immediately
11691196
// User can click "Steckdose hinzufügen" when they're done editing to hide the edit UI
@@ -1227,7 +1254,7 @@ export default function SocketGenerator() {
12271254

12281255
// Calculate position of first socket's bottom-left corner
12291256
const firstSocketLeft = socketGroup.positionX - ANCHOR_OFFSET_CM;
1230-
const firstSocketBottom = socketGroup.positionY + ANCHOR_OFFSET_CM;
1257+
const firstSocketBottom = socketGroup.positionY - ANCHOR_OFFSET_CM;
12311258

12321259
return {
12331260
// 'left' property: distance from left edge

lib/types.ts

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -134,27 +134,52 @@ export const validateSocketPosition = (
134134
// Anchor point is at 3.5cm from both left and bottom edges of the 7x7cm first socket
135135
// positionX and positionY are distances from plate edges TO the anchor point
136136
const ANCHOR_OFFSET_CM = 3.5;
137-
137+
138138
// Calculate the actual position of the socket group's boundaries
139139
// Anchor is at positionX from left and positionY from bottom
140140
// First socket's left edge = positionX - ANCHOR_OFFSET_CM (3.5cm left of anchor)
141-
// First socket's bottom edge = positionY + ANCHOR_OFFSET_CM (3.5cm down from anchor)
141+
// First socket's bottom edge = positionY - ANCHOR_OFFSET_CM (3.5cm up from anchor)
142142
// Group's left edge = first socket's left edge
143143
// Group's bottom edge = first socket's bottom edge
144144
// Group's right edge = first socket's left edge + groupWidth
145145
// Group's top edge = first socket's bottom edge + groupHeight
146146
const groupLeftX = positionX - ANCHOR_OFFSET_CM;
147-
const groupBottomY = positionY + ANCHOR_OFFSET_CM;
147+
const groupBottomY = positionY - ANCHOR_OFFSET_CM;
148148
const groupRightX = groupLeftX + groupWidth;
149149
const groupTopY = groupBottomY + groupHeight;
150150

151151
// Check if socket group fits within plate bounds
152152
// Task Requirement: "Socket groups must be at least 3 cm from plate edges"
153153
// Logic: Check if socket group is at least 3 cm from plate edges
154-
if (groupLeftX < MIN_EDGE_DISTANCE || groupBottomY < MIN_EDGE_DISTANCE) {
154+
if (groupLeftX < MIN_EDGE_DISTANCE) {
155+
const minAllowedGroupLeftX = MIN_EDGE_DISTANCE;
156+
const minAllowedPositionX = minAllowedGroupLeftX + ANCHOR_OFFSET_CM;
157+
return {
158+
valid: false,
159+
error: `Steckdosengruppe zu nah am linken Rand. Minimale Position von links: ${minAllowedPositionX.toFixed(
160+
1
161+
)} cm (Rückwandbreite: ${plate.width.toFixed(
162+
1
163+
)} cm, Steckdosengruppenbreite: ${groupWidth.toFixed(
164+
1
165+
)} cm, Mindestabstand: ${MIN_EDGE_DISTANCE} cm)`,
166+
positionX,
167+
positionY,
168+
};
169+
}
170+
171+
if (groupBottomY < MIN_EDGE_DISTANCE) {
172+
const minAllowedGroupBottomY = MIN_EDGE_DISTANCE;
173+
const minAllowedPositionY = minAllowedGroupBottomY + ANCHOR_OFFSET_CM;
155174
return {
156175
valid: false,
157-
error: `Steckdosengruppen müssen mindestens ${MIN_EDGE_DISTANCE} cm von den Rückwandrändern entfernt sein`,
176+
error: `Steckdosengruppe zu nah am unteren Rand. Minimale Position von unten: ${minAllowedPositionY.toFixed(
177+
1
178+
)} cm (Rückwandhöhe: ${plate.height.toFixed(
179+
1
180+
)} cm, Steckdosengruppenhöhe: ${groupHeight.toFixed(
181+
1
182+
)} cm, Mindestabstand: ${MIN_EDGE_DISTANCE} cm)`,
158183
positionX,
159184
positionY,
160185
};
@@ -184,7 +209,8 @@ export const validateSocketPosition = (
184209
if (groupTopY > plate.height - MIN_EDGE_DISTANCE) {
185210
const maxAllowedGroupTopY = plate.height - MIN_EDGE_DISTANCE;
186211
const maxAllowedGroupBottomY = maxAllowedGroupTopY - groupHeight;
187-
const maxAllowedPositionY = maxAllowedGroupBottomY - ANCHOR_OFFSET_CM;
212+
// Anchor is 3.5cm above socket bottom, so positionY = groupBottomY + 3.5
213+
const maxAllowedPositionY = maxAllowedGroupBottomY + ANCHOR_OFFSET_CM;
188214
return {
189215
valid: false,
190216
error: `Steckdosengruppe überschreitet die Rückwandhöhe. Maximale Position von unten: ${maxAllowedPositionY.toFixed(
@@ -221,13 +247,13 @@ export const validateSocketPosition = (
221247
// Rectangle 1: current group (socketGroup) - using bottom-left coordinate system
222248
const r1Left = positionX - ANCHOR_OFFSET_CM;
223249
const r1Right = r1Left + groupWidth;
224-
const r1Bottom = positionY + ANCHOR_OFFSET_CM;
250+
const r1Bottom = positionY - ANCHOR_OFFSET_CM;
225251
const r1Top = r1Bottom + groupHeight;
226252

227253
// Rectangle 2: other group (other) - using bottom-left coordinate system
228254
const r2Left = other.positionX - ANCHOR_OFFSET_CM;
229255
const r2Right = r2Left + otherWidth;
230-
const r2Bottom = other.positionY + ANCHOR_OFFSET_CM;
256+
const r2Bottom = other.positionY - ANCHOR_OFFSET_CM;
231257
const r2Top = r2Bottom + otherHeight;
232258

233259
// Calculate horizontal distance (x-axis)
@@ -258,7 +284,9 @@ export const validateSocketPosition = (
258284
if (totalDistance < MIN_GROUP_DISTANCE) {
259285
return {
260286
valid: false,
261-
error: `Steckdosengruppen müssen mindestens ${MIN_GROUP_DISTANCE} cm voneinander entfernt sein`,
287+
error: `Steckdosengruppen müssen mindestens ${MIN_GROUP_DISTANCE} cm voneinander entfernt sein. Aktueller Abstand: ${totalDistance.toFixed(
288+
1
289+
)} cm (Mindestabstand: ${MIN_GROUP_DISTANCE} cm)`,
262290
positionX,
263291
positionY,
264292
};

0 commit comments

Comments
 (0)