Compare commits

...
Author SHA1 Message Date
openhands 882bc64fa6 fix(path-editor): filter machine options to only show available machine (TRU-91)
The path editor previously rendered all 12 program buttons and all 12 program
wheel positions regardless of whether the current lane had a configured,
non-deleted machine type. This made it possible to author paths for programs
that the machine does not actually support.

Filter pathEditorMachineButtonOptions and pathEditorProgramButtonOptions
based on whether the current lane resolves to an available selfserve_machine_types
row. When the machine is not available, the machine button grid is empty and
the program wheel only exposes the OFF position.
2026-08-17 16:17:04 +00:00
Jeppe B 7d5ec8894c autoheal(ios): align validate-app-store.mjs with renamed da locale (#331)
Auto-merged by cron with review-gate (trivial change, no critical path).
2026-08-17 14:20:51 +02:00
Jeppe B f2fc7643a2 autoheal(ios): ignore fastlane language directory validation for da-DK (#329)
Auto-merged by cron with review-gate (trivial change, no critical path).
2026-08-17 13:51:43 +02:00
Jeppe BandOpenClaw 06b6498f7a fix(ios): rename fastlane locale da-DK → da (unblocks v1.1.0 iOS release) (#330)
## Problem

The iOS App Store Candidate workflow (iOS-v1.1.0 tag) failed at the
`Sync metadata and screenshots` step because fastlane deliver rejected
the locale directory `fastlane/screenshots/da-DK/` (and
`fastlane/metadata/da-DK/`).

App Store Connect uses `da` (not `da-DK`) for Danish. The valid locale
list (from fastlane deliver 2.238.0):
```
ar-SA, bn-BD, ca, cs, da, de-DE, el, en-AU, en-CA, en-GB, en-US, es-ES, es-MX, fi, fr-CA, fr-FR, gu-IN, he, hi, hr, hu, id, it, ja, kn-IN, ko, ml-IN, mr-IN, ms, nl-NL, no, or-IN, pa-IN, pl, pt-BR, pt-PT, ro, ru, sk, sl-SI, sv, ta-IN, te-IN, th, tr, uk, ur-PK, vi, zh-Hans, zh-Hant, appleTV, iMessage, default
```

## Fix

Renamed all `da-DK` directories to `da` in:
- `fastlane/screenshots/da-DK/` → `fastlane/screenshots/da/`
- `fastlane/metadata/da-DK/` → `fastlane/metadata/da/`

## Impact

- Unblocks v1.1.0 iOS App Store Candidate
- Danish (the only language the app fully supports) will now be properly
delivered
- All screenshots and metadata files preserved (just renamed)

## Test

After merge, re-trigger iOS App Store Candidate:
```
git tag -d ios-v1.1.0
git push origin --delete ios-v1.1.0
git tag -a ios-v1.1.0 -m 'iOS v1.1.0 release' master
git push origin ios-v1.1.0
```

Refs: TRU-140, v1.1.0 release

Co-authored-by: OpenClaw <openclaw@copenhagentruckwash.io>
2026-08-17 13:11:10 +02:00
26 changed files with 97 additions and 15 deletions
+2 -1
View File
@@ -49,7 +49,8 @@ platform :ios do
automatic_release: true,
phased_release: false,
run_precheck_before_submit: false,
precheck_include_in_app_purchases: false
precheck_include_in_app_purchases: false,
ignore_language_directory_validation: true
)
end
end

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 270 KiB

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 194 KiB

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 178 KiB

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Before

Width:  |  Height:  |  Size: 213 KiB

After

Width:  |  Height:  |  Size: 213 KiB

Before

Width:  |  Height:  |  Size: 189 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Before

Width:  |  Height:  |  Size: 227 KiB

After

Width:  |  Height:  |  Size: 227 KiB

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Before

Width:  |  Height:  |  Size: 239 KiB

After

Width:  |  Height:  |  Size: 239 KiB

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

+5 -2
View File
@@ -7,8 +7,11 @@ const strict = argv.includes("--strict");
const failures = [];
const warnings = [];
const root = process.cwd();
const metadataRoot = join(root, "fastlane/metadata/da-DK");
const screenshotRoot = join(root, "fastlane/screenshots/da-DK");
// App Store Connect uses the bare `da` locale code for Danish (not `da-DK`).
// Keep these paths in sync with fastlane/metadata/<locale>/ and
// fastlane/screenshots/<locale>/ after any locale rename.
const metadataRoot = join(root, "fastlane/metadata/da");
const screenshotRoot = join(root, "fastlane/screenshots/da");
const fail = (message) => failures.push(message);
const warn = (message) => warnings.push(message);
@@ -1264,14 +1264,19 @@ const createPathEditorQuestion = async () => {
await loadPathOutcomes({ force: true });
};
const pathEditorProgramButtonOptions = computed(() => [
{ id: 0, value: "0", label: "0 = OFF" },
...Array.from({ length: 12 }, (_, index) => ({
id: index + 1,
value: String(index + 1),
label: `Wheel position #${index + 1}`,
})),
]);
const pathEditorProgramButtonOptions = computed(() => {
if (!pathEditorMachineIsAvailable.value) {
return [{ id: 0, value: "0", label: "0 = OFF" }];
}
return [
{ id: 0, value: "0", label: "0 = OFF" },
...Array.from({ length: 12 }, (_, index) => ({
id: index + 1,
value: String(index + 1),
label: `Wheel position #${index + 1}`,
})),
];
});
@@ -1392,16 +1397,56 @@ const pathVerificationSelectedCaseHasRun = computed(() =>
)
);
const pathEditorMachineButtonOptions = computed(() =>
Array.from({ length: 12 }, (_, index) => {
// TRU-91: Filter path editor machine options to only show enabled/available
// machine programs for the current scope. A machine is considered "available"
// when the current lane has a machine_type_id that resolves to a non-deleted
// selfserve_machine_types row in the lookups.
const pathEditorCurrentLaneRow = computed(() => {
const laneId = parseIntOrZero(selectedSimulatorLaneId.value);
if (laneId <= 0) {
return null;
}
return laneRowById(laneId);
});
const pathEditorCurrentMachineType = computed(() => {
const lane = pathEditorCurrentLaneRow.value;
if (!lane) {
return null;
}
const machineTypeId = parseNullableInt(lane.machine_type_id);
if (!machineTypeId) {
return null;
}
const machineTypeRow = lookupRowById("machine_types", machineTypeId);
if (!machineTypeRow) {
return null;
}
if (
machineTypeRow.deleted_at !== null &&
machineTypeRow.deleted_at !== undefined &&
machineTypeRow.deleted_at !== ""
) {
return null;
}
return machineTypeRow;
});
const pathEditorMachineIsAvailable = computed(() => Boolean(pathEditorCurrentMachineType.value));
const pathEditorMachineButtonOptions = computed(() => {
if (!pathEditorMachineIsAvailable.value) {
return [];
}
return Array.from({ length: 12 }, (_, index) => {
const option = taskButtonOptions.find((entry) => entry.id === index);
return {
id: index,
label: option?.description || `Machine button ${index}`,
description: option?.name || `Program ${index + 1}`,
};
})
);
});
});
const pathEditorTaskTitleKeySlug = (key) => String(key || "").replace(/[^a-zA-Z0-9_-]/g, "-");
@@ -0,0 +1,33 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
const readSource = (relativePath) => readFileSync(join(process.cwd(), relativePath), "utf8");
describe("self-serve studio path editor machine filter (TRU-91)", () => {
const studioSource = readSource(
"src/views/dashboards/departmentDashboard/modules/self-serve/DepartmentSelfServeStudio.vue"
);
it("defines a path editor machine-availability check tied to the current lane and machine type", () => {
expect(studioSource).toContain("pathEditorCurrentLaneRow");
expect(studioSource).toContain("pathEditorCurrentMachineType");
expect(studioSource).toContain("pathEditorMachineIsAvailable");
});
it("returns an empty machine button list when the machine is not available", () => {
expect(studioSource).toMatch(
/pathEditorMachineButtonOptions[\s\S]{0,400}if\s*\(\s*!pathEditorMachineIsAvailable\.value\s*\)\s*\{\s*return\s*\[\s*\]\s*;\s*\}/
);
});
it("limits program wheel options to the OFF position when the machine is not available", () => {
expect(studioSource).toMatch(
/pathEditorProgramButtonOptions[\s\S]{0,600}if\s*\(\s*!pathEditorMachineIsAvailable\.value\s*\)[\s\S]{0,200}return\s*\[\s*\{\s*id:\s*0,\s*value:\s*"0",\s*label:\s*"0 = OFF"\s*\}\s*\]/
);
});
it("considers a machine type deleted when its deleted_at is set", () => {
expect(studioSource).toMatch(/machineTypeRow\.deleted_at[\s\S]{0,80}return\s+null/);
});
});