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
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
}
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
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
Expand All @@ -10,6 +13,9 @@ 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 Down
31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,34 @@ Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introdu

## 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/tdei-tools/main/docs/imagery-layer/schema.json
export VITE_IMAGERY_EXAMPLE_URL=https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/main/docs/imagery-layer/example.json
export VITE_LONG_FORM_QUEST_SCHEMA=https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/refs/heads/main/docs/quest-definition/schema.json
export VITE_LONG_FORM_QUEST_EXAMPLE_URL=https://raw.githubusercontent.com/TaskarCenterAtUW/tdei-tools/refs/heads/main/docs/quest-definition/example.json
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({ 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>

16 changes: 11 additions & 5 deletions components/DatasetTypeRadio.vue
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
<template>
<div class="btn-group">
<input
id="radio_type_osw"
v-model="model"
type="radio"
id="radio_type_osw"
name="radio_type"
class="btn-check"
autocomplete="off"
value="osw"
>
<label class="btn btn-outline-primary" for="radio_type_osw">OpenSidewalks</label>
<label
class="btn btn-outline-primary"
for="radio_type_osw"
>OpenSidewalks</label>

<input
id="radio_type_pathways"
v-model="model"
type="radio"
id="radio_type_pathways"
name="radio_type"
class="btn-check"
autocomplete="off"
value="pathways"
>
<label class="btn btn-outline-primary" for="radio_type_pathways">GTFS Pathways</label>
<label
class="btn btn-outline-primary"
for="radio_type_pathways"
>GTFS Pathways</label>
</div>
</template>

<script setup lang="ts">
const model = defineModel({ required: true });
const model = defineModel({ required: true })
</script>
5 changes: 4 additions & 1 deletion components/LoadingSpinner.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<template>
<div class="spinner-border" role="status">
<div
class="spinner-border"
role="status"
>
<span class="visually-hidden">Loading...</span>
</div>
</template>
Expand Down
Loading