Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
296417f
begin tasks + fix authorized views
IslandRhythms May 6, 2025
d725ccc
link to npm if no task package
IslandRhythms May 6, 2025
4c8f9dc
Merge branch 'main' into IslandRhythms/task-visualizer
IslandRhythms Jun 6, 2025
5bbc944
v1 task overview
IslandRhythms Jun 6, 2025
d32696c
remove task creator for v1
IslandRhythms Jun 6, 2025
3d920a0
Merge branch 'main' into IslandRhythms/task-visualizer
vkarpov15 Jun 30, 2025
9ddb57c
make requested changes
IslandRhythms Aug 29, 2025
fe02cfd
Merge branch 'main' into IslandRhythms/task-visualizer
IslandRhythms Aug 29, 2025
974a9b8
cleanup warnings and errors in console
IslandRhythms Aug 29, 2025
538b843
fix: lint
IslandRhythms Aug 29, 2025
86d3e59
fix css
IslandRhythms Aug 29, 2025
451045b
Update navbar.html
IslandRhythms Aug 29, 2025
5f57994
task details
IslandRhythms Aug 29, 2025
7f04242
more frontend work
IslandRhythms Aug 29, 2025
386b5a8
cleanup
IslandRhythms Aug 29, 2025
6e104bb
UI polishing
IslandRhythms Aug 29, 2025
4cd32ea
missed a spot
IslandRhythms Aug 29, 2025
c066231
remove unused functions
IslandRhythms Aug 29, 2025
9e2f44d
remove unnecessary watcher
IslandRhythms Aug 29, 2025
a4fd90f
fix: lint
IslandRhythms Aug 29, 2025
29c9bdf
complete task visualizer
IslandRhythms Sep 2, 2025
684d890
fix: lint
IslandRhythms Sep 2, 2025
8a27ee2
Update eslint.config.js
IslandRhythms Sep 26, 2025
24dd8ca
fix: model navigation not working
IslandRhythms Oct 2, 2025
c55eba7
Update models.js
IslandRhythms Oct 2, 2025
8b3943a
Merge branch 'main' into IslandRhythms/task-visualizer
vkarpov15 Oct 3, 2025
fd26468
manually fix lint
IslandRhythms Oct 7, 2025
57da3ce
Merge branch 'main' into IslandRhythms/task-visualizer
IslandRhythms Oct 15, 2025
5a994ee
better UX
IslandRhythms Oct 15, 2025
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
38 changes: 38 additions & 0 deletions backend/actions/Task/getTasks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict';

const Archetype = require('archetype');

const GetTasksParams = new Archetype({
start: {

Check warning on line 6 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4

Check warning on line 6 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4
$type: Date

Check warning on line 7 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 4 spaces but found 8

Check warning on line 7 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 4 spaces but found 8
},

Check warning on line 8 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4

Check warning on line 8 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4
end: {

Check warning on line 9 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4

Check warning on line 9 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4
$type: Date

Check warning on line 10 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 4 spaces but found 8

Check warning on line 10 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 4 spaces but found 8
},

Check warning on line 11 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4

Check warning on line 11 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4
status: {

Check warning on line 12 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4

Check warning on line 12 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4
$type: 'string'

Check warning on line 13 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 4 spaces but found 8

Check warning on line 13 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 4 spaces but found 8
}

Check warning on line 14 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4

Check warning on line 14 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Expected indentation of 2 spaces but found 4
}).compile('GetTasksParams');

module.exports = ({ db }) => async function getTasks(params) {
params = new GetTasksParams(params);
const { start, end, status } = params;
const { Task } = db.models;

const filter = {};

if (start && end) {
filter.scheduledAt = { $gte: start, $lte: end };
} else if (start) {
filter.scheduledAt = { $gte: start }

Check failure on line 27 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon

Check failure on line 27 in backend/actions/Task/getTasks.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon
}
if (status) {
filter.status = status;
}

const tasks = await Task.find(filter);

return {
tasks
};
};
3 changes: 3 additions & 0 deletions backend/actions/Task/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict';

exports.getTasks = require('./getTasks')

Check failure on line 3 in backend/actions/Task/index.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon

Check failure on line 3 in backend/actions/Task/index.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon
1 change: 1 addition & 0 deletions backend/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
exports.Model = require('./Model');
exports.Script = require('./Script');
exports.status = require('./status');
exports.Task = require('./Task')

Check failure on line 9 in backend/actions/index.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon

Check failure on line 9 in backend/actions/index.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon
10 changes: 10 additions & 0 deletions frontend/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@
return client.post('', { action: 'Model.updateDocuments', ...params }).then(res => res.data);
}
};
exports.Task = {
getTasks: function getTasks(params) {
return client.post('', { action: 'Task.getTasks', ...params}).then(res => res.data)

Check failure on line 132 in frontend/src/api.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon

Check warning on line 132 in frontend/src/api.js

View workflow job for this annotation

GitHub Actions / lint

A space is required before '}'

Check failure on line 132 in frontend/src/api.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon

Check warning on line 132 in frontend/src/api.js

View workflow job for this annotation

GitHub Actions / lint

A space is required before '}'
}
}

Check failure on line 134 in frontend/src/api.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon

Check failure on line 134 in frontend/src/api.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon
} else {
exports.status = function status() {
return client.get('/status').then(res => res.data);
Expand Down Expand Up @@ -229,4 +234,9 @@
return client.post('/Model/updateDocuments', params).then(res => res.data);
}
};
exports.Task = {
getTasks: function getTasks(params) {
return client.post('/Task/getTasks', params).then(res => res.data);
}
}

Check failure on line 241 in frontend/src/api.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon

Check failure on line 241 in frontend/src/api.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon
}
33 changes: 32 additions & 1 deletion frontend/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ app.component('app-component', {
}
});

const { routes } = require('./routes');
const { routes, hasAccess } = require('./routes');
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes: routes.map(route => ({
Expand All @@ -136,6 +136,37 @@ const router = VueRouter.createRouter({
}))
});

// Add global navigation guard
router.beforeEach((to, from, next) => {
// Skip auth check for authorized (public) routes
if (to.meta.authorized) {
Comment on lines +146 to +147
Copy link

Copilot AI Jun 20, 2025

Choose a reason for hiding this comment

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

The global navigation guard's logic appears inverted: routes marked as 'authorized' are immediately permitted without an access check, while those with 'authorized' set to false require role validation. Please verify that the flag's semantics and its usage in routes match the intended access control behavior.

Suggested change
// Skip auth check for authorized (public) routes
if (to.meta.authorized) {
// Skip auth check for public routes (not authorized)
if (!to.meta.authorized) {

Copilot uses AI. Check for mistakes.
next();
return;
}

// Get roles from the app state
const roles = window.state?.roles;

// Check if user has access to the route
if (!hasAccess(roles, to.name)) {
// Find all routes the user has access to
const allowedRoutes = routes.filter(route => hasAccess(roles, route.name));

// If user has no allowed routes, redirect to splash/login
if (allowedRoutes.length === 0) {
next({ name: 'root' });
return;
}

// Redirect to first allowed route
const firstAllowedRoute = allowedRoutes[0].name;
next({ name: firstAllowedRoute });
return;
}

next();
});

app.use(router);

app.mount('#content');
4 changes: 4 additions & 0 deletions frontend/src/navbar/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
href="#/chat"
class="inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium"
:class="chatView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'">Chat</a>
<a
:href="hasTasks"
class="inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium"
:class="dashboardView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'">Tasks</a>

<div class="h-full flex items-center" v-if="!user && hasAPIKey">
<button
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/navbar/navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@
},
defaultRoute() {
return this.allowedRoutes[0]?.name || 'dashboards';
},
hasTasks() {
// fix this when done
try {
require.resolve('@mongoosejs/task');
return `#/tasks`

Check failure on line 74 in frontend/src/navbar/navbar.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon

Check failure on line 74 in frontend/src/navbar/navbar.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon
} catch (e) {
return `https://www.npmjs.com/package/@mongoosejs/task`

Check failure on line 76 in frontend/src/navbar/navbar.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon

Check failure on line 76 in frontend/src/navbar/navbar.js

View workflow job for this annotation

GitHub Actions / lint

Missing semicolon
}
}
},
methods: {
Expand Down
29 changes: 22 additions & 7 deletions frontend/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ const roleAccess = {

// Helper function to check if a role has access to a route
function hasAccess(roles, routeName) {
// change to true for local development
if (!roles) return false;
return roles.some(role => roleAccess[role]?.includes(routeName));
// If no roles are provided, deny access
if (!roles || !Array.isArray(roles)) return false;

// Check if any of the user's roles grant access to this route
return roles.some(role => {
// If the role doesn't exist in roleAccess, deny access
if (!roleAccess[role]) return false;
// Check if the role has access to this route
return roleAccess[role].includes(routeName);
});
}

module.exports = {
Expand All @@ -31,37 +38,45 @@ module.exports = {
name: 'model',
component: 'models',
meta: {
authorized: true
authorized: false
}
},
{
path: '/model/:model/document/:documentId',
name: 'document',
component: 'document',
meta: {
authorized: true
authorized: false
}
},
{
path: '/dashboards',
name: 'dashboards',
component: 'dashboards',
meta: {
authorized: true
authorized: false
}
},
{
path: '/dashboard/:dashboardId',
name: 'dashboard',
component: 'dashboard',
meta: {
authorized: true
authorized: false
}
},
{
path: '/team',
name: 'team',
component: 'team',
meta: {
authorized: false
}
},
{
path: '/tasks',
name: 'tasks',
component: 'tasks',
meta: {
authorized: true
}
Expand Down
Empty file added frontend/src/tasks/tasks.css
Empty file.
63 changes: 63 additions & 0 deletions frontend/src/tasks/tasks.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<div class="p-4 space-y-6">
<h1 class="text-2xl font-bold text-gray-700">Task Overview</h1>
<!-- Task List -->
<div class="bg-white p-4 rounded-lg shadow">
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Filter by Date:</label>
<select v-model="selectedRange" @change="updateDateRange" class="border-gray-300 rounded-md shadow-sm w-full p-2">
<option v-for="option in dateFilters" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Filter by Status:</label>
<select v-model="selectedStatus" @change="getTasks" class="border-gray-300 rounded-md shadow-sm w-full p-2">
<option v-for="option in statusFilters" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
</div>
<div class="mb-6">
<button
@click="resetFilters"
class="w-full bg-gray-200 text-gray-700 hover:bg-gray-300 font-medium py-2 px-4 rounded-md transition"
>
Reset Filters
</button>
</div>
<!-- Summary Section -->
<div class="grid grid-cols-2 sm:grid-cols-4 gap-4">
<div class="bg-yellow-100 text-yellow-800 p-4 rounded-lg shadow">
<div class="text-sm">Scheduled</div>
<div class="text-2xl font-bold">{{pendingCount}}</div>
</div>
<div class="bg-green-100 text-green-800 p-4 rounded-lg shadow">
<div class="text-sm">Completed</div>
<div class="text-2xl font-bold">{{succeededCount}}</div>
</div>
<div class="bg-red-100 text-red-800 p-4 rounded-lg shadow">
<div class="text-sm">Failed</div>
<div class="text-2xl font-bold">{{failedCount}}</div>
</div>
<div class="bg-blue-100 text-blue-800 p-4 rounded-lg shadow">
<div class="text-sm">Cancelled</div>
<div class="text-2xl font-bold">{{cancelledCount}}</div>
</div>
</div>
<ul class="divide-y divide-gray-200">
<li v-for="task in tasks" :key="task.id" class="py-3 flex items-center justify-between">
<div>
<div class="font-medium">{{ task.name }}</div>
<div class="text-sm text-gray-500">{{ new Date(task.scheduledAt).toLocaleString() }}</div>
</div>
<span
class="text-xs px-2 py-1 rounded-full font-medium"
:class="getStatusColor(task.status)"
>
{{ task.status }}
</span>
</li>
</ul>
</div>
</div>
Loading
Loading