Skip to content

Commit

Permalink
Implement basic layout
Browse files Browse the repository at this point in the history
  • Loading branch information
NoelDeMartin committed Jan 19, 2024
1 parent 3e2b8f3 commit 15a89ce
Show file tree
Hide file tree
Showing 27 changed files with 357 additions and 160 deletions.
1 change: 1 addition & 0 deletions cypress/e2e/onboarding.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe('Onboarding', () => {
// Assert
cy.dontSee('Solid Focus');
cy.see('Main');
cy.see('Inbox (active)');
cy.see('Start being more focused');
});

Expand Down
251 changes: 134 additions & 117 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<AGAppLayout class="bg-blue-50">
<main class="flex flex-grow flex-col items-center justify-center">
<AppHome v-if="$workspaces.current" :workspace="$workspaces.current" />
<AppMain v-if="$workspaces.current && $tasksLists.current" />
<AppOnboarding v-else />
</main>
</AGAppLayout>
Expand Down
28 changes: 0 additions & 28 deletions src/components/AppHome.vue

This file was deleted.

6 changes: 6 additions & 0 deletions src/components/AppMain.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<div class="flex w-full flex-grow">
<MainSidebar />
<MainContent class="flex-1" />
</div>
</template>
6 changes: 4 additions & 2 deletions src/components/AppOnboarding.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
<script setup lang="ts">
import { requiredStringInput, translate, useForm } from '@aerogel/core';
import Workspace from '@/models/Workspace';
import Task from '@/models/Task';
import TasksList from '@/models/TasksList';
import Workspace from '@/models/Workspace';
import Workspaces from '@/services/Workspaces';
const form = useForm({
Expand All @@ -30,9 +31,10 @@ const form = useForm({
async function submit(): Promise<void> {
const task = await Task.create({ name: form.draft });
const list = await TasksList.create({ name: 'Inbox', taskIds: [task.id] });
const workspace = await Workspace.create({
name: translate('onboarding.workspaceName'),
taskIds: [task.id],
listIds: [list.id],
});
Workspaces.open(workspace);
Expand Down
6 changes: 6 additions & 0 deletions src/components/main/MainContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<div>
<MainContentHeader />
<MainContentBody />
</div>
</template>
5 changes: 5 additions & 0 deletions src/components/main/MainContentBody.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div class="px-4 py-6">
<TasksList :tasks="$tasksList.tasks" />
</div>
</template>
5 changes: 5 additions & 0 deletions src/components/main/MainContentHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div class="bg-gray-300 px-4 py-6">
<h1>{{ $tasksList.name }}</h1>
</div>
</template>
6 changes: 6 additions & 0 deletions src/components/main/MainSidebar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<div class="flex flex-col border-r border-gray-400 bg-gray-200">
<MainSidebarHeader />
<MainSidebarNav />
</div>
</template>
5 changes: 5 additions & 0 deletions src/components/main/MainSidebarHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div class="bg-gray-300 px-4 py-6">
{{ $workspace.name }}
</div>
</template>
10 changes: 10 additions & 0 deletions src/components/main/MainSidebarNav.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<template>
<nav class="px-4 py-6">
<ul>
<li v-for="list of $workspace.lists" :key="list.id">
{{ list.name }}
<span v-if="$tasksList.id === list.id">(active)</span>
</li>
</ul>
</nav>
</template>
15 changes: 15 additions & 0 deletions src/components/tasks/TasksList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<ul v-if="tasks.length">
<TasksListItem v-for="task of tasks" :key="task.id" :task="task" />
</ul>

<AGMarkdown v-else lang-key="tasks.empty" />
</template>

<script setup lang="ts">
import { requiredArrayProp } from '@aerogel/core';
import type Task from '@/models/Task';
defineProps({ tasks: requiredArrayProp<Task>() });
</script>
13 changes: 13 additions & 0 deletions src/components/tasks/TasksListItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<li>
{{ task.name }}
</li>
</template>

<script setup lang="ts">
import { requiredObjectProp } from '@aerogel/core';
import type Task from '@/models/Task';
defineProps({ task: requiredObjectProp<Task>() });
</script>
3 changes: 3 additions & 0 deletions src/lang/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ onboarding:
draft_placeholder: E.g. learn Japanese
submit: Create task
workspaceName: Main

tasks:
empty: You don't have any tasks, create a new one!
5 changes: 4 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import { bootstrap } from '@aerogel/core';

import './assets/css/styles.css';
import App from './App.vue';
import { services } from './services';
import { globals, services } from './services';

bootstrap(App, {
services,
plugins: [
i18n({ messages: import.meta.glob('@/lang/*.yaml') }),
soukai({ models: import.meta.glob(['@/models/*', '!**/*.test.ts'], { eager: true }) }),
],
install(app) {
Object.assign(app.config.globalProperties, globals);
},
});
5 changes: 4 additions & 1 deletion src/models/Task.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { FieldType, defineModelSchema } from 'soukai';

export default defineModelSchema({
fields: {
name: FieldType.String,
name: {
type: FieldType.String,
required: true,
},
},
});
14 changes: 14 additions & 0 deletions src/models/TasksList.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { FieldType, defineModelSchema } from 'soukai';

export default defineModelSchema({
fields: {
name: {
type: FieldType.String,
required: true,
},
taskIds: {
type: FieldType.Array,
items: FieldType.Key,
},
},
});
16 changes: 16 additions & 0 deletions src/models/TasksList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { BelongsToManyRelation, Relation } from 'soukai';

import Task from '@/models/Task';

import Model from './TasksList.schema';

export default class TasksList extends Model {

public declare tasks?: Task[];
public declare relatedTasks: BelongsToManyRelation<this, Task, typeof Task>;

public tasksRelationship(): Relation {
return this.belongsToMany(Task);
}

}
2 changes: 1 addition & 1 deletion src/models/Workspace.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FieldType, defineModelSchema } from 'soukai';
export default defineModelSchema({
fields: {
name: FieldType.String,
taskIds: {
listIds: {
type: FieldType.Array,
items: FieldType.Key,
},
Expand Down
10 changes: 5 additions & 5 deletions src/models/Workspace.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import type { BelongsToManyRelation, Relation } from 'soukai';

import Task from '@/models/Task';
import TasksList from '@/models/TasksList';

import Model from './Workspace.schema';

export default class Workspace extends Model {

public declare tasks?: Task[];
public declare relatedTasks: BelongsToManyRelation<this, Task, typeof Task>;
public declare lists?: TasksList[];
public declare relatedLists: BelongsToManyRelation<this, TasksList, typeof TasksList>;

public tasksRelationship(): Relation {
return this.belongsToMany(Task);
public listsRelationship(): Relation {
return this.belongsToMany(TasksList, 'listIds');
}

}
19 changes: 19 additions & 0 deletions src/services/TasksLists.state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { defineServiceState } from '@aerogel/core';
import type { Key } from 'soukai';

import Workspaces from '@/services/Workspaces';

export default defineServiceState({
name: 'tasks-lists',
persist: ['currentTasksListId'],
initialState: {
currentTasksListId: null as Key | null,
},
computed: {
current({ currentTasksListId }) {
const lists = Workspaces.current?.lists ?? [];

return lists.find((list) => list.id === currentTasksListId) ?? null;
},
},
});
38 changes: 38 additions & 0 deletions src/services/TasksLists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { facade } from '@noeldemartin/utils';
import { watchEffect } from 'vue';

import Workspaces from '@/services/Workspaces';
import type TasksList from '@/models/TasksList';

import Service from './TasksLists.state';

export class TasksListsService extends Service {

public async open(list: TasksList): Promise<void> {
await list.loadRelationIfUnloaded('tasks');

this.currentTasksListId = list.id;
}

protected async boot(): Promise<void> {
await Workspaces.booted;

this.current && (await this.open(this.current));

watchEffect(async () => {
const lists = Workspaces.current?.lists ?? [];
const current = lists.find((list) => list.id === this.currentTasksListId) ?? lists[0] ?? null;

if (!current) {
this.currentTasksListId = null;

return;
}

await this.open(current);
});
}

}

export default facade(new TasksListsService());
5 changes: 3 additions & 2 deletions src/services/Workspaces.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export default defineServiceState({
workspaces: [] as Workspace[],
},
computed: {
current: ({ currentWorkspaceId, workspaces }) =>
workspaces?.find((workspace) => workspace.id === currentWorkspaceId),
current({ currentWorkspaceId, workspaces }) {
return workspaces?.find((workspace) => workspace.id === currentWorkspaceId);
},
},
});
6 changes: 5 additions & 1 deletion src/services/Workspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import Service from './Workspaces.state';

export class WorkspacesService extends Service {

public open(workspace: Workspace): void {
public async open(workspace: Workspace): Promise<void> {
await workspace.loadRelationIfUnloaded('lists');

this.currentWorkspaceId = workspace.id;
}

Expand All @@ -16,6 +18,8 @@ export class WorkspacesService extends Service {
service: this,
property: 'workspaces',
});

this.current && (await this.open(this.current));
}

}
Expand Down
12 changes: 11 additions & 1 deletion src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { lazyRequireProxy } from '@/utils';

import Workspaces from './Workspaces';
import TasksLists from './TasksLists';

export const globals = {
$workspace: lazyRequireProxy(() => Workspaces.current, 'Current workspace is missing, can\'t use $workspace'),
$tasksList: lazyRequireProxy(() => TasksLists.current, 'Current tasks list is missing, can\'t use $tasksList'),
};

export const services = {
$workspaces: Workspaces,
$tasksLists: TasksLists,
};

export type AppGlobals = typeof globals;
export type AppServices = typeof services;

declare module '@vue/runtime-core' {
interface ComponentCustomProperties extends AppServices {}
interface ComponentCustomProperties extends AppGlobals, AppServices {}
}
23 changes: 23 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { fail } from '@noeldemartin/utils';
import type { ValueWithoutEmpty } from '@noeldemartin/utils';

export function lazyRequireProxy<T extends object | null | undefined>(
get: () => T,
message: string = 'Lazy required value is missing',
): ValueWithoutEmpty<T> {
return new Proxy(
{},
{
get(_, property, receiver) {
const model = get() ?? fail<ValueWithoutEmpty<T>>(message);

return Reflect.get(model, property, receiver);
},
set(_, property, value, receiver) {
const model = get() ?? fail<ValueWithoutEmpty<T>>(message);

return Reflect.set(model, property, value, receiver);
},
},
) as ValueWithoutEmpty<T>;
}

0 comments on commit 15a89ce

Please sign in to comment.