Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ VITE_OSM_URL=https://osm.workspaces-dev.sidewalks.washington.edu/
# --- Embedded Editors ---------------------------------------------------------
# Rapid editor (OSW editing) and Pathways editor (GTFS Pathways editing).
# These are standalone apps hosted separately.
VITE_RAPID_URL=https://rapid.workspaces-dev.sidewalks.washington.edu/
VITE_RAPID3_URL=https://rapid.workspaces-dev.sidewalks.washington.edu/rapid3/
VITE_RAPID_URL=https://rapid.workspaces-dev.sidewalks.washington.edu/rapid2/
VITE_PATHWAYS_EDITOR_URL=https://pathways.workspaces-dev.sidewalks.washington.edu/

# --- Schema / Example URLs (rarely need to change) ---------------------------
Expand Down
34 changes: 33 additions & 1 deletion components/dashboard/Toolbar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
<template>
<div class="btn-toolbar">
<nuxt-link class="btn btn-dark" :to="editRoute">
<b-dropdown
v-if="isOsw && rapid3Available"
split
variant="dark"
:split-to="editRoute"
>
<template #button-content>
<app-icon
variant="edit_location_alt"
size="24"
/>
Edit
</template>
<b-dropdown-item :to="editRoute">
Rapid 2 (default)
</b-dropdown-item>
<b-dropdown-item :to="editRouteRapid3">
Rapid 3 (beta)
</b-dropdown-item>
</b-dropdown>
<nuxt-link v-else class="btn btn-dark" :to="editRoute">
<app-icon variant="edit_location_alt" size="24" />
Edit
</nuxt-link>
Expand Down Expand Up @@ -37,6 +57,10 @@
</template>

<script setup lang="ts">
import { rapid3Manager } from '~/services/index'

const rapid3Available = !!rapid3Manager

const props = defineProps({
workspace: {
type: Object,
Expand All @@ -54,12 +78,20 @@ const editHash = computed(() => {
return `#map=${zoom}/${latitude}/${longitude}`;
});

const isOsw = computed(() => props.workspace.type === 'osw');

const editRoute = computed(() => ({
path: workspacePath('edit'),
query: { datatype: props.workspace.type },
hash: editHash.value
}));

const editRouteRapid3 = computed(() => ({
path: workspacePath('edit'),
query: { datatype: props.workspace.type, editor: 'rapid3' },
hash: editHash.value
}));

const reviewRoute = computed(() => workspacePath('review'));
const exportRoute = computed(() => workspacePath('export'));
const settingsRoute = computed(() => workspacePath('settings'));
Expand Down
17 changes: 15 additions & 2 deletions pages/workspace/[id]/edit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,33 @@
</template>

<script setup lang="ts">
import { pathwaysManager, rapidManager } from '~/services/index'
import { pathwaysManager, rapidManager, rapid3Manager } from '~/services/index'

const route = useRoute()
const workspaceId = route.params.id
const datatype = route.query.datatype
const editor = route.query.editor
const editorContainer = ref(null)
const manager = datatype === 'osw' ? rapidManager : pathwaysManager

const oswManager = (editor === 'rapid3' && rapid3Manager) ? rapid3Manager : rapidManager
const manager = datatype === 'osw' ? oswManager : pathwaysManager

function onEditorLoaded() {
editorContainer.value.appendChild(manager.containerNode)
manager.init(workspaceId)
Comment on lines 12 to 22
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Type mismatch: workspaceId is a string but init() expects a number.

route.params.id returns a string, but Rapid3Manager.init(workspaceId: number) expects a numeric argument. This could cause issues with strict type checks or if the workspace ID is used in numeric comparisons.

🛡️ Proposed fix to parse workspaceId
 const route = useRoute()
-const workspaceId = route.params.id
+const workspaceId = Number(route.params.id)
 const datatype = route.query.datatype
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/workspace/`[id]/edit.vue around lines 12 - 22, route.params.id is a
string but manager.init expects a number; convert and validate it before calling
manager.init in onEditorLoaded (reference workspaceId, onEditorLoaded,
manager.init, route.params.id) — parse the id to a Number (or parseInt) into a
new variable (e.g., workspaceIdNum), check for NaN and handle the error case
(log/throw or early return), then pass the numeric value to manager.init instead
of the raw workspaceId string.

}

onMounted(() => {
// If a different Rapid version is already loaded, hard-reload to swap.
// Only one version can occupy the global Rapid namespace at a time.
if (datatype === 'osw') {
const otherManager = manager === rapidManager ? rapid3Manager : rapidManager
if (otherManager?.loaded.value) {
window.location.reload()
return
}
}

if (!manager.loaded.value) {
watch(manager.loaded, (val) => {
if (val) {
Expand Down
109 changes: 56 additions & 53 deletions services/index.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,56 @@
import { reactive } from 'vue';
import { TdeiAuthStore, TdeiClient, TdeiUserClient } from '~/services/tdei';
import {
AugmentedDiffCache,
AugmentedDiffDb,
OsmChangeCache,
OsmChangeDb,
ChangesetManager,
} from '~/services/changesets';
import { OsmApiClient } from '~/services/osm';
import { PathwaysEditorManager } from '~/services/pathways';
import { ReviewManager } from '~/services/review';
import { RapidManager } from '~/services/rapid';
import { WorkspacesClient } from '~/services/workspaces';

const tdeiApiUrl = import.meta.env.VITE_TDEI_API_URL;
const tdeiUserApiUrl = import.meta.env.VITE_TDEI_USER_API_URL;
const apiUrl = import.meta.env.VITE_API_URL;
const newApiUrl = import.meta.env.VITE_NEW_API_URL;
const osmWebUrl = import.meta.env.VITE_OSM_URL;
const osmApiUrl = osmWebUrl + 'api/0.6/';
const rapidUrl = import.meta.env.VITE_RAPID_URL;
const pathwaysUrl = import.meta.env.VITE_PATHWAYS_EDITOR_URL;

export const tdeiAuth = reactive(new TdeiAuthStore());
export const tdeiClient = new TdeiClient(tdeiApiUrl, tdeiAuth);
export const tdeiUserClient = new TdeiUserClient(tdeiUserApiUrl, tdeiClient);
tdeiClient.restartAutoAuthRefresh();

export const osmClient = new OsmApiClient(osmWebUrl, osmApiUrl, tdeiClient);
export const workspacesClient = new WorkspacesClient(apiUrl, newApiUrl, tdeiClient, osmClient);

const oscCacheTtl = 1000 * 60 * 60 * 24 * 45; // 45 days
const adiffCacheTtl = oscCacheTtl;
const oscKeypath = ['workspaceId', 'changesetId'];
const adiffKeypath = oscKeypath;

const oscCacheDb = new OsmChangeDb('osc-cache', oscCacheTtl, oscKeypath);
const adiffCacheDb = new AugmentedDiffDb('adiff-cache', adiffCacheTtl, adiffKeypath);

export const changesetManager = new ChangesetManager(
osmClient,
new OsmChangeCache(oscCacheDb),
new AugmentedDiffCache(adiffCacheDb));
export const reviewManager = new ReviewManager(
changesetManager,
osmClient,
tdeiClient,
workspacesClient,
);

export const rapidManager = new RapidManager(rapidUrl, osmWebUrl, tdeiAuth);
export const pathwaysManager = new PathwaysEditorManager(pathwaysUrl, osmWebUrl, tdeiAuth);
import { reactive } from 'vue';
import { TdeiAuthStore, TdeiClient, TdeiUserClient } from '~/services/tdei';
import {
AugmentedDiffCache,
AugmentedDiffDb,
OsmChangeCache,
OsmChangeDb,
ChangesetManager,
} from '~/services/changesets';
import { OsmApiClient } from '~/services/osm';
import { PathwaysEditorManager } from '~/services/pathways';
import { ReviewManager } from '~/services/review';
import { RapidManager } from '~/services/rapid';
import { Rapid3Manager } from '~/services/rapid3';
import { WorkspacesClient } from '~/services/workspaces';

const tdeiApiUrl = import.meta.env.VITE_TDEI_API_URL;
const tdeiUserApiUrl = import.meta.env.VITE_TDEI_USER_API_URL;
const apiUrl = import.meta.env.VITE_API_URL;
const newApiUrl = import.meta.env.VITE_NEW_API_URL;
const osmWebUrl = import.meta.env.VITE_OSM_URL;
const osmApiUrl = osmWebUrl + 'api/0.6/';
const rapidUrl = import.meta.env.VITE_RAPID_URL;
const rapid3Url = import.meta.env.VITE_RAPID3_URL;
const pathwaysUrl = import.meta.env.VITE_PATHWAYS_EDITOR_URL;

export const tdeiAuth = reactive(new TdeiAuthStore());
export const tdeiClient = new TdeiClient(tdeiApiUrl, tdeiAuth);
export const tdeiUserClient = new TdeiUserClient(tdeiUserApiUrl, tdeiClient);
tdeiClient.restartAutoAuthRefresh();

export const osmClient = new OsmApiClient(osmWebUrl, osmApiUrl, tdeiClient);
export const workspacesClient = new WorkspacesClient(apiUrl, newApiUrl, tdeiClient, osmClient);

const oscCacheTtl = 1000 * 60 * 60 * 24 * 45; // 45 days
const adiffCacheTtl = oscCacheTtl;
const oscKeypath = ['workspaceId', 'changesetId'];
const adiffKeypath = oscKeypath;

const oscCacheDb = new OsmChangeDb('osc-cache', oscCacheTtl, oscKeypath);
const adiffCacheDb = new AugmentedDiffDb('adiff-cache', adiffCacheTtl, adiffKeypath);

export const changesetManager = new ChangesetManager(
osmClient,
new OsmChangeCache(oscCacheDb),
new AugmentedDiffCache(adiffCacheDb));
export const reviewManager = new ReviewManager(
changesetManager,
osmClient,
tdeiClient,
workspacesClient,
);

export const rapidManager = new RapidManager(rapidUrl, osmWebUrl, tdeiAuth);
export const rapid3Manager = rapid3Url ? new Rapid3Manager(rapid3Url, osmWebUrl, tdeiAuth) : null;
export const pathwaysManager = new PathwaysEditorManager(pathwaysUrl, osmWebUrl, tdeiAuth);
Loading
Loading