diff --git a/backend/LexBoxApi/GraphQL/LexQueries.cs b/backend/LexBoxApi/GraphQL/LexQueries.cs index 82a53e305..8607d0524 100644 --- a/backend/LexBoxApi/GraphQL/LexQueries.cs +++ b/backend/LexBoxApi/GraphQL/LexQueries.cs @@ -36,6 +36,14 @@ public IQueryable Projects(LexBoxDbContext context, bool withDeleted = } } + [UseProjection] + [UseSorting] + public IQueryable MyDraftProjects(LoggedInContext loggedInContext, LexBoxDbContext context) + { + var userId = loggedInContext.User.Id; + return context.DraftProjects.Where(p => p.ProjectManagerId == userId); + } + [UseProjection] [UseFiltering] [UseSorting] diff --git a/frontend/schema.graphql b/frontend/schema.graphql index 7555f3af2..b29fa036f 100644 --- a/frontend/schema.graphql +++ b/frontend/schema.graphql @@ -273,6 +273,7 @@ type ProjectUsers { type Query { myProjects(orderBy: [ProjectSortInput!]): [Project!]! + myDraftProjects(orderBy: [DraftProjectSortInput!]): [DraftProject!]! projects(withDeleted: Boolean! = false where: ProjectFilterInput orderBy: [ProjectSortInput!]): [Project!]! @authorize(policy: "AdminRequiredPolicy") draftProjects(where: DraftProjectFilterInput orderBy: [DraftProjectSortInput!]): [DraftProject!]! @authorize(policy: "AdminRequiredPolicy") projectById(projectId: UUID!): Project @@ -889,4 +890,4 @@ scalar UUID scalar timestamptz @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") -scalar uuid \ No newline at end of file +scalar uuid diff --git a/frontend/src/lib/components/ProjectList.svelte b/frontend/src/lib/components/ProjectList.svelte index 564446a24..8e0d11d0b 100644 --- a/frontend/src/lib/components/ProjectList.svelte +++ b/frontend/src/lib/components/ProjectList.svelte @@ -1,42 +1,64 @@ -
+
{#each projects as project} - -
-
-

- - {project.name} - -

- -

{project.code}

- -

-

- -

- {#if project.lastCommit} - {$t('projectlist.last_change', { - lastChange: new Date(project.lastCommit), + {#if project.isDraft} +

+
+
+

+ + {project.name} + + +

+

{project.code}

+ + + {$t('project.awaiting_approval')} + +

+ {$t('projectlist.requested', { + requested: new Date(project.createdDate), })} - {:else} - - {$t('projectlist.no_changes')} - - {/if} -

+

+
-
+ {:else} + + @@ -50,6 +72,9 @@ hover:border-neutral hover:shadow-xl; + max-height: 50vh; + max-width: 100%; + .bg { @apply absolute w-full @@ -73,4 +98,9 @@ @apply opacity-100; } } + + .draft.card { + pointer-events: none; + box-shadow: none; + } diff --git a/frontend/src/lib/components/Projects/ProjectTable.svelte b/frontend/src/lib/components/Projects/ProjectTable.svelte index 4c5bf722a..7b713e378 100644 --- a/frontend/src/lib/components/Projects/ProjectTable.svelte +++ b/frontend/src/lib/components/Projects/ProjectTable.svelte @@ -106,6 +106,8 @@ {:else if !project.isDraft} {$date(project.lastCommit)} + {:else} + {$t('project.awaiting_approval')} {/if} {/if} diff --git a/frontend/src/lib/i18n/locales/en.json b/frontend/src/lib/i18n/locales/en.json index b584ee0ba..1a0e14c75 100644 --- a/frontend/src/lib/i18n/locales/en.json +++ b/frontend/src/lib/i18n/locales/en.json @@ -213,6 +213,7 @@ the [Linguistics Institute at Payap University](https://li.payap.ac.th/) in Chia "project_now_not_confidential": "Project is now not confidential.", } }, + "awaiting_approval": "Awaiting approval" }, "project_page": { "project": "Project", @@ -399,7 +400,8 @@ If you don't see a dialog or already closed it, click the button below:", "projectlist": { "last_change": "Last Change: {lastChange, date, short} {lastChange, time, short}", "no_changes": "New", - "shared_with": "Shared with {memberCount, plural, one {no one} other {# people} }" + "shared_with": "Shared with {memberCount, plural, one {no one} other {# people} }", + "requested": "Requested: {requested, date, short} {requested, time, short}", }, "register": { "title": "Register", diff --git a/frontend/src/routes/(authenticated)/+page.svelte b/frontend/src/routes/(authenticated)/+page.svelte index b5a81b2ae..61ace8c55 100644 --- a/frontend/src/routes/(authenticated)/+page.svelte +++ b/frontend/src/routes/(authenticated)/+page.svelte @@ -5,7 +5,7 @@ import { HeaderPage } from '$lib/layout'; import { getSearchParams, queryParam } from '$lib/util/query-params'; import type { ProjectType } from '$lib/gql/types'; - import { ProjectFilter, filterProjects, type ProjectFilters, type ProjectItem } from '$lib/components/Projects'; + import { ProjectFilter, filterProjects, type ProjectFilters, type ProjectItemWithDraftStatus } from '$lib/components/Projects'; import ProjectTable from '$lib/components/Projects/ProjectTable.svelte'; import { Button } from '$lib/forms'; import { limit } from '$lib/components/Paging'; @@ -15,6 +15,7 @@ export let data: PageData; $: projects = data.projects; + $: draftProjects = data.draftProjects; type Filters = Pick; @@ -23,9 +24,22 @@ projectType: queryParam.string(undefined), }); + let allProjects: ProjectItemWithDraftStatus[] = []; + let filteredProjects: ProjectItemWithDraftStatus[] = []; + let limitResults = true; + $: allProjects = [ + ...$draftProjects.map(p => ({ + ...p, isDraft: true as const, + createUrl: '' + })), + ...$projects.map(p => ({ ...p, isDraft: false as const })), + ]; + $: filteredProjects = filterProjects(allProjects, $filters); + $: shownProjects = limitResults ? limit(filteredProjects) : filteredProjects; + let initializedMode = false; let mode: ViewMode; - $: defaultMode = $projects.length < 10 ? ViewMode.Grid : ViewMode.Table; + $: defaultMode = allProjects.length < 10 ? ViewMode.Grid : ViewMode.Table; $: { if (!initializedMode) { @@ -43,11 +57,6 @@ mode = selectedMode; Cookies.set(STORAGE_VIEW_MODE_KEY, mode, { expires: 365 * 10 }); } - - let filteredProjects: ProjectItem[] = []; - let limitResults = true; - $: filteredProjects = filterProjects($projects, $filters); - $: shownProjects = limitResults ? limit(filteredProjects) : filteredProjects; @@ -84,7 +93,7 @@ {/if} - {#if !$projects.length} + {#if !allProjects.length}
{#if !data.user.emailVerified && !data.user.createdByAdmin} diff --git a/frontend/src/routes/(authenticated)/+page.ts b/frontend/src/routes/(authenticated)/+page.ts index 226cd09ac..9fffff5fe 100644 --- a/frontend/src/routes/(authenticated)/+page.ts +++ b/frontend/src/routes/(authenticated)/+page.ts @@ -19,6 +19,20 @@ export async function load(event: PageLoadEvent) { type isConfidential } + + myDraftProjects(orderBy: [ + {code: ASC } + ]) { + code + createdDate + id + name + type + description + retentionPolicy + isConfidential + projectManagerId + } } `), {}); @@ -26,6 +40,7 @@ export async function load(event: PageLoadEvent) { return { projects: results.myProjects, + draftProjects: results.myDraftProjects, projectViewMode, } } diff --git a/frontend/tailwind.config.cjs b/frontend/tailwind.config.cjs index cd5682ec4..660a5b97b 100644 --- a/frontend/tailwind.config.cjs +++ b/frontend/tailwind.config.cjs @@ -1,4 +1,6 @@ const { iconsPlugin, getIconCollections } = require('@egoist/tailwindcss-icons'); +const defaultTheme = require('tailwindcss/defaultTheme'); + /** @type {import('tailwindcss').Config} */ module.exports = { content: ['./src/**/*.{svelte,ts}'], @@ -32,6 +34,10 @@ module.exports = { logs: false, }, theme: { + screens: { + 'xs': '400px', + ...defaultTheme.screens, + }, extend: { screens: { 'max-xs': { 'max': '400px' },