Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
e62e455
Added imagery JSON URL field under External Apps section in workspace…
srikanth-vg Sep 1, 2025
74c6c71
Updated readme with adding new environment var VITE_IMAGERY_SCHEMA
srikanth-vg Sep 1, 2025
f729523
Implemented adding imeagery JSON definition insted of URL, enabled dr…
srikanth-vg Sep 2, 2025
bff59f0
Updated App name to AVIV ScoutRoute
srikanth-vg Sep 2, 2025
1fcacf2
Implemented drag and drop the json file for long form quests filed.
srikanth-vg Sep 2, 2025
a5fb0d8
Updated sending API data in json object insted of string
srikanth-vg Sep 3, 2025
882f05d
Refactored imagery list initialization and updated tdeiMetadata handl…
iamrajeshk Sep 7, 2025
56bcae4
Added Json validation for Long form quests JSON with respective schema
srikanth-vg Sep 9, 2025
fbe924c
updated external apps setting save button title to Save
srikanth-vg Sep 9, 2025
e1a328f
Updated tost messages on save changes
srikanth-vg Sep 9, 2025
3e1ac75
removed rel="noopener" in ancor tag
srikanth-vg Sep 9, 2025
d1d8db7
removed unused toggleExternalAppAccess function
srikanth-vg Sep 11, 2025
3057b15
updated sending and expecting Json string insted of object for imager…
srikanth-vg Sep 11, 2025
cad7f3f
removed toasts and showing success or error messages below the fields
srikanth-vg Sep 11, 2025
4e41f3b
Implemented handling imageryListDef from workspaces/[id] API insted o…
srikanth-vg Sep 11, 2025
822aa1f
Removed get long form API dependancy as we are getting long form data…
srikanth-vg Sep 11, 2025
b831e9e
removed saveLongFormQuestDefinition as it is not using
srikanth-vg Sep 11, 2025
4ba2136
Remove unused vulnerable deps (#13)
cyrossignol Sep 18, 2025
24b9e6e
updated ref useage
srikanth-vg Sep 22, 2025
c817894
Added imagery JSON URL field under External Apps section in workspace…
srikanth-vg Sep 22, 2025
7d9614b
Add support for long form quest URLs
cyrossignol Oct 15, 2025
403f8d0
Add support for long form quest URLs (#17)
jeffmaki Nov 4, 2025
ed07918
Add workspace review queue
cyrossignol Nov 17, 2025
c116fb2
Sentry integration
jeffmaki Dec 16, 2025
7246d7a
Update Dockerfile
jeffmaki Dec 16, 2025
d72276c
DSN update
jeffmaki Dec 16, 2025
2306117
Update
jeffmaki Dec 16, 2025
9430129
Update Dockerfile
jeffmaki Dec 16, 2025
e9e0914
Update Dockerfile
jeffmaki Dec 17, 2025
675dd50
Merge remote-tracking branch 'origin/review-queue' into jeff-sentry-i…
jeffmaki Dec 19, 2025
1d66721
Add back sentry after merge
jeffmaki Dec 19, 2025
858ae6a
Imagery method API
jeffmaki Dec 31, 2025
abf9998
Revert merge of review-queue
jeffmaki Dec 31, 2025
cfad5fb
Linter fixes
jeffmaki Dec 31, 2025
e6b2c3e
ESLint config for nuxt
jeffmaki Dec 31, 2025
b4a7eee
Update settings.vue
jeffmaki Dec 31, 2025
05e69fd
Linter
jeffmaki Dec 31, 2025
a15d03f
Linter
jeffmaki Dec 31, 2025
4ce7750
Update nuxt.config.ts
jeffmaki Dec 31, 2025
4cf8e57
Update nuxt.config.ts
jeffmaki Dec 31, 2025
e2d50a8
Sentry integration (#22)
jeffmaki Dec 31, 2025
ca7ba09
CI/CD linter
jeffmaki Jan 6, 2026
75953e2
CI/CD linter (#25)
jeffmaki Jan 6, 2026
03399a1
Test
jeffmaki Jan 6, 2026
e9af005
Test (#26)
jeffmaki Jan 6, 2026
769f32a
Update ci.yml
jeffmaki Jan 6, 2026
e2ac656
Update ci.yml (#27)
jeffmaki Jan 6, 2026
7f7deb4
Update README.md
jeffmaki Jan 6, 2026
64aea20
Update README.md (#28)
jeffmaki Jan 6, 2026
dfdbf97
Typescript updates
jeffmaki Jan 27, 2026
27ceb6a
Linter
jeffmaki Jan 27, 2026
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
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: CI

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
run-lint:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm ci

- name: Run lint script
run: npm run lint
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"biome.enabled": false,
// Required in vscode-eslint < v3.0.10 only
"eslint.useFlatConfig": true
}
12 changes: 11 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
FROM node as builder

# These are ARGs because nuxt bakes them into its minified version of the JS,
# So they are actually set at build time vs. run-time
#
ARG VITE_TDEI_API_URL
ARG VITE_TDEI_USER_API_URL
ARG VITE_API_URL
ARG VITE_OSM_URL
ARG VITE_RAPID_URL
ARG VITE_PATHWAYS_EDITOR_URL
ARG VITE_IMAGERY_SCHEMA
ARG VITE_IMAGERY_EXAMPLE_URL
ARG VITE_LONG_FORM_QUEST_SCHEMA
ARG VITE_LONG_FORM_QUEST_EXAMPLE_URL
ARG VITE_SENTRY_AUTH_TOKEN
ARG VITE_SENTRY_DSN

ARG CODE_VERSION=unknown

WORKDIR /app/
Expand All @@ -24,4 +34,4 @@ RUN echo "This is (frontend, cgimap, osmrails, pathways, rapid, taskingmanager)

RUN chown -R nginx:nginx /usr/share/nginx/html/

COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,38 @@

Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.

### ⚠️ Reminder: you must set the tag of the environment you wish to deploy in this repo, then run the deploy workflow in workspaces-stack to deploy to dev, stage or prod.
### ⚠️ Reminder: you must set the tag of the environment you wish to deploy in this repo, then run the deploy workflow in workspaces-stack to deploy to dev, stage or prod.

## Dev Setup

NB: This will start the dev server against the cloud-hosted backend. If you need to change both the frontend and the backend, you'll
need to start a local copy of the backend (workspaces-tasking-manager) and set the environment vars appropriately below.

The below values are for cloud-hosted dev:

```
# set these e.g. to the dev TDEI instance--note: any tokens you get from these hosts must match the environment for other components
# e.g. you can't use dev TDEI KeyCloak JWT tokens in stage or production environments.
export VITE_TDEI_API_URL=https://api-dev.tdei.us/api/v1/
export VITE_TDEI_USER_API_URL=https://portal-api-dev.tdei.us/api/v1/
export VITE_API_URL=https://api.workspaces-dev.sidewalks.washington.edu/api/v1/
export VITE_OSM_URL=https://osm.workspaces-dev.sidewalks.washington.edu/

# use your local workspaces-backend instance--or set to the dev instance of this component if not running locally
export VITE_API_URL=http://localhost:3000/api/v1/
export VITE_OSM_URL=http://localhost:3000/

# probably want to leave these as-is
export VITE_RAPID_URL=https://rapid.workspaces-dev.sidewalks.washington.edu/
export VITE_PATHWAYS_EDITOR_URL=https://pathways.workspaces-dev.sidewalks.washington.edu/

# probably don't need to change any of these
export CODE_VERSION="local"
export VITE_IMAGERY_SCHEMA=https://raw.githubusercontent.com/TaskarCenterAtUW/asr-imagery-list/refs/heads/main/schema/schema.json
export VITE_IMAGERY_EXAMPLE_URL=https://github.com/TaskarCenterAtUW/asr-imagery-list/blob/main/examples/example.json
export VITE_LONG_FORM_QUEST_SCHEMA=https://raw.githubusercontent.com/TaskarCenterAtUW/asr-quests/refs/heads/main/schema/schema.json
export VITE_LONG_FORM_QUEST_EXAMPLE_URL=https://raw.githubusercontent.com/TaskarCenterAtUW/asr-quests/refs/heads/main/docs/quest-definition/example.json

# install deps
# install deps (first time only)
npm install

# start dev server
npm run dev
```

## Troubleshooting

If you run ```npm run dev``` and nothing happens, check that you've set your "exports" as above. Undefined environment variables are not handled gracefully right now.
10 changes: 5 additions & 5 deletions components/AppIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@
const props = defineProps({
variant: {
type: String,
required: true
required: true,
},
size: {
type: [Number, String],
default: 18
default: 18,
},
noMargin: {
type: Boolean,
default: false
}
default: false,
},
})

const classes = computed(() => ([
'material-icons',
`md-${props.size}`,
`md-${props.variant}`,
props.noMargin ? undefined : 'me-2'
props.noMargin ? undefined : 'me-2',
]))
</script>

Expand Down
7 changes: 5 additions & 2 deletions components/AppLogo.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<template>
<img class="app-logo img-fluid" src="~/assets/img/icon.svg" alt="" />
<img
class="app-logo img-fluid"
src="~/assets/img/icon.svg"
alt=""
>
</template>

89 changes: 73 additions & 16 deletions components/AppNavbar.vue
Original file line number Diff line number Diff line change
@@ -1,42 +1,99 @@
<template>
<nav class="app-navbar navbar navbar-expand-md shadow">
<div class="container-lg">
<nuxt-link class="navbar-brand" to="/">
<nuxt-link
class="navbar-brand"
to="/"
>
<app-logo />
<span>TDEI</span>&nbsp;<span>Workspaces</span>
</nuxt-link>

<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon" />
</button>

<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul v-show="auth.ok" class="navbar-nav me-auto mb-2 mb-lg-0">
<div
id="navbarSupportedContent"
class="collapse navbar-collapse"
>
<ul
v-show="auth.ok"
class="navbar-nav me-auto mb-2 mb-lg-0"
>
<li class="nav-item">
<nuxt-link class="nav-link" aria-current="page" to="/">Home</nuxt-link>
<nuxt-link
class="nav-link"
aria-current="page"
to="/"
>Home</nuxt-link>
</li>
<li class="nav-item">
<nuxt-link class="nav-link" to="/dashboard">Dashboard</nuxt-link>
<nuxt-link
class="nav-link"
to="/dashboard"
>Dashboard</nuxt-link>
</li>
<li class="nav-item">
<nuxt-link class="nav-link" to="/workspace/create">Create Workspace</nuxt-link>
<nuxt-link
class="nav-link"
to="/workspace/create"
>Create Workspace</nuxt-link>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Help</a>
<a
class="nav-link"
href="#"
>Help</a>
</li>
</ul>

<span class="mx-auto" />

<nuxt-link v-show="!auth.ok" to="/signin" class="btn">Sign In</nuxt-link>
<span v-show="auth.ok" class="nav-item dropdown">
<a class="nav-link" href="#" id="navbarAccountMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<app-icon variant="account_circle" size="24" />{{ auth.displayName }}
<nuxt-link
v-show="!auth.ok"
to="/signin"
class="btn"
>Sign In</nuxt-link>
<span
v-show="auth.ok"
class="nav-item dropdown"
>
<a
id="navbarAccountMenuLink"
class="nav-link"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<app-icon
variant="account_circle"
size="24"
/>{{ auth.displayName }}
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarAccountMenuLink">
<ul
class="dropdown-menu dropdown-menu-end"
aria-labelledby="navbarAccountMenuLink"
>
<li>
<nuxt-link class="dropdown-item" to="/" @click="auth.clear()">
<app-icon variant="logout" class="me-3" />Logout
<nuxt-link
class="dropdown-item"
to="/"
@click="auth.clear()"
>
<app-icon
variant="logout"
class="me-3"
/>Logout
</nuxt-link>
</li>
</ul>
Expand Down
9 changes: 6 additions & 3 deletions components/AppSpinner.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
<template>
<div :class="classes" role="status">
<div
:class="classes"
role="status"
>
<span class="visually-hidden">Loading...</span>
</div>
</template>

<script setup lang="ts">
const props = defineProps({
size: String
size: String,
})

const classes = computed(() => ({
'spinner-border': true,
'spinner-border-sm': props.size === 'sm',
'spinner-border-lg': props.size === 'lg'
'spinner-border-lg': props.size === 'lg',
}))
</script>
79 changes: 47 additions & 32 deletions components/DatasetPicker.vue
Original file line number Diff line number Diff line change
@@ -1,43 +1,58 @@
<template>
<input type="text" v-model="searchText" placeholder="Start typing a dataset name to filter datasets..." aria-label="Dataset Filter" class="form-control" />
<select
v-model="model"
class="dataset-picker form-select"
aria-label="Dataset Selection"
<input
v-model="searchText"
type="text"
placeholder="Start typing a dataset name to filter datasets..."
aria-label="Dataset Filter"
class="form-control"
>
<select
v-model="model"
class="dataset-picker form-select"
aria-label="Dataset Selection"
>
<option
:selected
value="null"
disabled
>
<option :selected value=null disabled>Select a (matching) dataset...</option>
<option v-for="ds in datasets" :key="ds.id" :value="ds.id" >
{{ ds.name }} (version {{ ds.version }})
</option>
</select>
Select a (matching) dataset...
</option>
<option
v-for="ds in datasets"
:key="ds.id"
:value="ds.id"
>
{{ ds.name }} (version {{ ds.version }})
</option>
</select>
</template>

<script setup lang="ts">
import { tdeiClient } from '~/services/index'
const model = defineModel({ required: true });
const props = defineProps({
projectGroupId: {
type: String,
required: true
}
});
import { tdeiClient } from '~/services/index'

const model = defineModel<string>({ required: true })
const props = defineProps({
projectGroupId: {
type: String,
required: true,
},
})

const { projectGroupId } = toRefs(props);
const searchText = ref('');
const datasets = ref([]);
refreshDatasets(projectGroupId.value, searchText.value);
const { projectGroupId } = toRefs(props)
const searchText = ref('')
const datasets = ref([])
refreshDatasets(projectGroupId.value, searchText.value)

watch(projectGroupId, (val) => refreshDatasets(val, searchText.value));
watch(searchText, (val) => refreshDatasets(projectGroupId.value, val));
watch(projectGroupId, val => refreshDatasets(val, searchText.value))
watch(searchText, val => refreshDatasets(projectGroupId.value, val))

async function refreshDatasets(projectGroupId: string, name: string) {
datasets.value = (await tdeiClient.getDatasetsByProjectGroupAndName(projectGroupId, name))
.sort((a, b) => a.name.localeCompare(b.name));
async function refreshDatasets(projectGroupId: string, name: string) {
datasets.value = (await tdeiClient.getDatasetsByProjectGroupAndName(projectGroupId, name))
.sort((a, b) => a.name.localeCompare(b.name))

if (datasets.value.length === 0) {
model.value = null;
}
if (datasets.value.length === 0) {
model.value = null
}
}
</script>

Loading
Loading