Browse Source

add API and GUI of moving requirements between projects

master
Gisle Aune 1 year ago
parent
commit
98175e4160
  1. 3
      entities/project.go
  2. 2
      frontend/src/lib/components/common/Modal.svelte
  3. 30
      frontend/src/lib/components/controls/ProjectSelect.svelte
  4. 2
      frontend/src/lib/components/scope/ProjectMenu.svelte
  5. 6
      frontend/src/lib/modals/RequirementCreateModal.svelte
  6. 1
      frontend/src/lib/models/project.ts
  7. 1
      models/project.go
  8. 5
      ports/mysql/mysqlcore/projects.sql.go
  9. 1
      ports/mysql/projects.go
  10. 3
      ports/mysql/queries/projects.sql
  11. 12
      usecases/projects/service.go

3
entities/project.go

@ -67,6 +67,9 @@ func (requirement *Requirement) Update(update models.RequirementUpdate) {
if update.AggregateRequired != nil {
requirement.AggregateRequired = *update.AggregateRequired
}
if update.ProjectID != nil {
requirement.ProjectID = *update.ProjectID
}
if update.RemoveTags != nil || update.AddTags != nil {
requirement.Tags = genutils.SliceWithout(requirement.Tags, update.RemoveTags)

2
frontend/src/lib/components/common/Modal.svelte

@ -176,7 +176,7 @@
padding: 0.45em 1ch;
width: 100%;
}
div.modal :global(select option) {
div.modal :global(select option), div.modal :global(select optgroup) {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;

30
frontend/src/lib/components/controls/ProjectSelect.svelte

@ -0,0 +1,30 @@
<script lang="ts">
import type { ProjectEntry } from "$lib/models/project";
import { STATUS_NAMES } from "$lib/models/status";
import type Status from "$lib/models/status";
import { getProjectListContext } from "../contexts/ProjectListContext.svelte";
import { STATUS_ORDER } from "../scope/ProjectMenu.svelte";
const { projects } = getProjectListContext();
export let value: number;
export let disabled: boolean = false;
let projectsByStatus: Record<Status, ProjectEntry[]>;
$: projectsByStatus = $projects.reduce((p, c) => (
{...p, [c.status]: [...p[c.status], c]}
), {0: [], 1: [], 2: [], 3: [], 4: [], 5: [], 6: []});
</script>
<select disabled={disabled} bind:value={value}>
{#each STATUS_ORDER as status (status)}
{#if projectsByStatus[status].length > 0}
<optgroup label={STATUS_NAMES[status]}>
{#each projectsByStatus[status] as project (project.id)}
<option value={project.id}>{project.name}</option>
{/each}
</optgroup>
{/if}
{/each}
</select>

2
frontend/src/lib/components/scope/ProjectMenu.svelte

@ -1,7 +1,7 @@
<script lang="ts" context="module">
import Status from "$lib/models/status";
const STATUS_ORDER = [
export const STATUS_ORDER = [
Status.Active,
Status.Background,
Status.Available,

6
frontend/src/lib/modals/RequirementCreateModal.svelte

@ -7,6 +7,7 @@
import { getModalContext } from "$lib/components/contexts/ModalContext.svelte";
import { getProjectContext } from "$lib/components/contexts/ProjectContext.svelte";
import { getScopeContext } from "$lib/components/contexts/ScopeContext.svelte";
import ProjectSelect from "$lib/components/controls/ProjectSelect.svelte";
import StatInput from "$lib/components/controls/StatInput.svelte";
import StatusSelect from "$lib/components/controls/StatusSelect.svelte";
import TagInput from "$lib/components/controls/TagInput.svelte";
@ -60,6 +61,7 @@
isCoarse: false,
aggregateRequired: 0,
tags: [],
projectId: project.id,
}
projectId = project.id;
@ -75,6 +77,7 @@
isCoarse: current.isCoarse,
aggregateRequired: current.aggregateRequired,
tags: [...current.tags],
projectId: project.id,
}
oldTags = [...current.tags];
@ -103,6 +106,7 @@
stats: statDiff(oldStats, requirement.stats, -1),
addTags: requirement.tags.filter(t => !oldTags.includes(t)),
removeTags: oldTags.filter(t => !requirement.tags.includes(t)),
projectId: requirement.projectId !== projectId ? requirement.projectId : void(0),
};
await sl3(fetch).updateRequirement($scope.id, projectId, requirementId, submission);
@ -140,6 +144,8 @@
<TagInput bind:value={requirement.tags} />
</ModalBody>
<ModalBody>
<label for="req">Project Requirement</label>
<ProjectSelect disabled={op === "Create"} bind:value={requirement.projectId} />
<label for="stats">Status</label>
<StatusSelect bind:status={requirement.status} />
<label for="stats">Stats</label>

1
frontend/src/lib/models/project.ts

@ -58,4 +58,5 @@ export interface RequirementInput {
tags: string[]
addTags?: string[]
removeTags?: string[]
projectId?: number
}

1
models/project.go

@ -17,4 +17,5 @@ type RequirementUpdate struct {
AggregateRequired *int `json:"aggregateRequired"`
AddTags []string `json:"addTags"`
RemoveTags []string `json:"removeTags"`
ProjectID *int `json:"projectId"`
}

5
ports/mysql/mysqlcore/projects.sql.go

@ -334,7 +334,8 @@ SET name = ?,
status = ?,
description = ?,
is_coarse = ?,
aggregate_required = ?
aggregate_required = ?,
project_id = ?
WHERE id = ? AND scope_id = ?
`
@ -344,6 +345,7 @@ type UpdateProjectRequirementParams struct {
Description string
IsCoarse bool
AggregateRequired int
ProjectID int
ID int
ScopeID int
}
@ -355,6 +357,7 @@ func (q *Queries) UpdateProjectRequirement(ctx context.Context, arg UpdateProjec
arg.Description,
arg.IsCoarse,
arg.AggregateRequired,
arg.ProjectID,
arg.ID,
arg.ScopeID,
)

1
ports/mysql/projects.go

@ -539,6 +539,7 @@ func (r *projectRepository) UpdateRequirement(ctx context.Context, requirement e
ID: requirement.ID,
ScopeID: requirement.ScopeID,
AggregateRequired: requirement.AggregateRequired,
ProjectID: requirement.ProjectID,
})
for _, tag := range update.RemoveTags {

3
ports/mysql/queries/projects.sql

@ -32,7 +32,8 @@ SET name = ?,
status = ?,
description = ?,
is_coarse = ?,
aggregate_required = ?
aggregate_required = ?,
project_id = ?
WHERE id = ? AND scope_id = ?;
-- name: DeleteProjectRequirement :exec

12
usecases/projects/service.go

@ -280,6 +280,18 @@ func (s *Service) UpdateRequirement(ctx context.Context, projectID, requirementI
return nil, err
}
if update.ProjectID != nil && *update.ProjectID != project.ID {
dstProject, err := s.Find(ctx, *update.ProjectID)
if err != nil {
return nil, err
}
if dstProject.ScopeID != project.ScopeID {
return nil, models.ForbiddenError("Requirement cannot be moved across scopes.")
}
project = dstProject
}
err = s.Repository.UpdateRequirement(ctx, req.Requirement, update)
if err != nil {
return nil, err

Loading…
Cancel
Save