Skip to content

Commit

Permalink
Implemented Goals -> Limitations feature
Browse files Browse the repository at this point in the history
  • Loading branch information
mlhaufe committed Dec 22, 2023
1 parent a3910d8 commit af552d4
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 7 deletions.
3 changes: 0 additions & 3 deletions _old/domain/entities/Limit.mts

This file was deleted.

8 changes: 8 additions & 0 deletions src/data/LimitRepository.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Limit from '~/domain/Limit.mjs';
import { LocalStorageRepository } from './LocalStorageRepository.mjs';
import LimitToJsonMapper from '~/mappers/LimitToJsonMapper.mjs';
import pkg from '~/../package.json' with { type: 'json' };

export default class UseCaseRepository extends LocalStorageRepository<Limit> {
constructor() { super('limits', new LimitToJsonMapper(pkg.version)); }
}
2 changes: 2 additions & 0 deletions src/domain/Goals.mts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default class Goals extends PEGS {
stakeholders: Uuid[];
situation: string;
useCases: Uuid[];
limits: Uuid[];

constructor(options: Properties<Goals>) {
super(options);
Expand All @@ -26,5 +27,6 @@ export default class Goals extends PEGS {
this.stakeholders = options.stakeholders;
this.situation = options.situation;
this.useCases = options.useCases;
this.limits = options.limits;
}
}
12 changes: 12 additions & 0 deletions src/domain/Limit.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Properties } from '~/types/Properties.mjs';
import Requirement from './Requirement.mjs';

/**
* A Limit is a requirement describing a property that is out-of-scope.
* Example: "Providing an interface to the user to change the color of the background is out-of-scope."
*/
export default class Limit extends Requirement {
constructor(properties: Properties<Limit>) {
super(properties);
}
}
7 changes: 5 additions & 2 deletions src/mappers/GoalsToJsonMapper.mts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface GoalsJson extends PEGSJson {
situation: string;
stakeholders: Uuid[];
useCases: Uuid[];
limits: Uuid[];
}

export default class GoalsToJsonMapper extends PEGSToJsonMapper {
Expand All @@ -18,7 +19,8 @@ export default class GoalsToJsonMapper extends PEGSToJsonMapper {
if (version.startsWith('0.3.'))
return new Goals({
...target,
useCases: target.useCases ?? []
useCases: target.useCases ?? [],
limits: target.limits ?? []
});

throw new Error(`Unsupported serialization version: ${version}`);
Expand All @@ -31,7 +33,8 @@ export default class GoalsToJsonMapper extends PEGSToJsonMapper {
outcomes: source.outcomes,
situation: source.situation,
stakeholders: source.stakeholders,
useCases: source.useCases
useCases: source.useCases,
limits: source.limits
};
}
}
18 changes: 18 additions & 0 deletions src/mappers/LimitToJsonMapper.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Limit from '~/domain/Limit.mjs';
import RequirementToJsonMapper, { type RequirementJson } from './RequirementToJsonMapper.mjs';

export interface LimitJson extends RequirementJson { }

export default class BehaviorToJsonMapper extends RequirementToJsonMapper {
override mapFrom(target: LimitJson): Limit {
const version = target.serializationVersion ?? '{undefined}';

if (version.startsWith('0.3.'))
return new Limit(target);

throw new Error(`Unsupported serialization version: ${version}`);
}
override mapTo(source: Limit): LimitJson {
return super.mapTo(source);
}
}
3 changes: 2 additions & 1 deletion src/presentation/Application.mts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ export default class Application extends Container {
['/goals/:slug/rationale', (await import('./pages/goals/Rationale.mjs')).Rationale],
['/goals/:slug/functionality', (await import('./pages/goals/Functionality.mjs')).Functionality],
['/goals/:slug/stakeholders', (await import('./pages/goals/Stakeholders.mjs')).Stakeholders],
['/goals/:slug/use-cases', (await import('./pages/goals/UseCases.mjs')).UseCases]
['/goals/:slug/use-cases', (await import('./pages/goals/UseCases.mjs')).UseCases],
['/goals/:slug/limitations', (await import('./pages/goals/Limitations.mjs')).Limitations],
]);
this.#router.addEventListener('route', this);
}
Expand Down
5 changes: 5 additions & 0 deletions src/presentation/pages/goals/Goal.mts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export class Goal extends Page {
title: 'Use Cases',
icon: 'briefcase',
href: `${location.pathname}/use-cases`
}),
new MiniCard({
title: 'Limitations',
icon: 'x-circle',
href: `${location.pathname}/limitations`
})
])
]);
Expand Down
68 changes: 68 additions & 0 deletions src/presentation/pages/goals/Limitations.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Limit from '~/domain/Limit.mjs';
import type Goals from '~/domain/Goals.mjs';
import GoalsRepository from '~/data/GoalsRepository.mjs';
import LimitRepository from '~/data/LimitRepository.mjs';
import html from '~/presentation/lib/html.mjs';
import { DataTable } from '~/presentation/components/DataTable.mjs';
import { SlugPage } from '../SlugPage.mjs';

const { p } = html;

export class Limitations extends SlugPage {
static {
customElements.define('x-limitations-page', this);
}

#goalsRepository = new GoalsRepository();
#limitRepository = new LimitRepository();
#goals?: Goals;

constructor() {
super({ title: 'Limitations' }, [
p([
`Limitations are the constraints on functionality.
They describe What that is out-of-scope and excluded.
Example: "Providing an interface to the user to change
the color of the background is out-of-scope."
`
])
]);

const dataTable = new DataTable<Limit>({
columns: {
id: { headerText: 'ID', readonly: true, formType: 'hidden' },
statement: { headerText: 'Statement', required: true, formType: 'text' }
},
select: async () => {
if (!this.#goals)
return [];

return await this.#limitRepository.getAll(l => this.#goals!.limits.includes(l.id));
},
onCreate: async item => {
const limit = new Limit({ ...item, id: self.crypto.randomUUID() });
await this.#limitRepository.add(limit);
this.#goals!.limits.push(limit.id);
await this.#goalsRepository.update(this.#goals!);
},
onUpdate: async item => {
const limit = (await this.#limitRepository.get(item.id))!;
limit.statement = item.statement;
await this.#limitRepository.update(limit);
},
onDelete: async id => {
await this.#limitRepository.delete(id);
this.#goals!.limits = this.#goals!.limits.filter(x => x !== id);
await this.#goalsRepository.update(this.#goals!);
}
});
this.append(dataTable);

this.#goalsRepository.addEventListener('update', () => dataTable.renderData());
this.#limitRepository.addEventListener('update', () => dataTable.renderData());
this.#goalsRepository.getBySlug(this.slug).then(goals => {
this.#goals = goals;
dataTable.renderData();
});
}
}
3 changes: 2 additions & 1 deletion src/presentation/pages/goals/NewGoals.mts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ export class NewGoals extends Page {
situation: '',
stakeholders: [],
functionalBehaviors: [],
useCases: []
useCases: [],
limits: []
});

await this.#repository.add(goals);
Expand Down

0 comments on commit af552d4

Please sign in to comment.