From cd52b8e684bdf6143848def8bef7cfaec8a5925f Mon Sep 17 00:00:00 2001 From: Vyacheslav Tumanov Date: Fri, 4 Aug 2023 23:07:04 +0500 Subject: [PATCH] UBER-619: StatusPopup for creating/renaming (#3536) Signed-off-by: Vyacheslav Tumanov --- plugins/task-assets/lang/en.json | 5 +- plugins/task-assets/lang/ru.json | 5 +- .../src/components/CreateStatePopup.svelte | 87 +++++++++ .../src/components/kanban/KanbanEditor.svelte | 32 +--- .../kanban/KanbanTemplateEditor.svelte | 32 +--- .../src/components/state/StatesEditor.svelte | 165 +++++++++++------- .../src/components/state/StatusesPopup.svelte | 29 ++- plugins/task-resources/src/index.ts | 4 +- plugins/task-resources/src/plugin.ts | 5 +- plugins/task/src/index.ts | 3 +- server-plugins/task-resources/src/index.ts | 2 +- server/core/src/storage.ts | 2 +- server/mongo/src/storage.ts | 2 +- tests/sanity/tests/settings.spec.ts | 24 ++- 14 files changed, 244 insertions(+), 153 deletions(-) create mode 100644 plugins/task-resources/src/components/CreateStatePopup.svelte diff --git a/plugins/task-assets/lang/en.json b/plugins/task-assets/lang/en.json index 7c2f7a98ce..db29a9721a 100644 --- a/plugins/task-assets/lang/en.json +++ b/plugins/task-assets/lang/en.json @@ -73,6 +73,9 @@ "TodoItems": "Todos", "Dashboard": "Dashboard", "AllTime": "All time", - "RelatedIssues": "Related processes" + "RelatedIssues": "Related processes", + "StatusName": "Status name", + "StatusPopupTitle": "Create new status or edit name for existing", + "NameAlreadyExists": "This name already exists for this status type" } } \ No newline at end of file diff --git a/plugins/task-assets/lang/ru.json b/plugins/task-assets/lang/ru.json index a023518463..76d0a63288 100644 --- a/plugins/task-assets/lang/ru.json +++ b/plugins/task-assets/lang/ru.json @@ -73,6 +73,9 @@ "TodoItems": "Todos", "Dashboard": "Дашборд", "AllTime": "Все время", - "RelatedIssues": "Связанные процессы" + "RelatedIssues": "Связанные процессы", + "StatusName": "Имя статуса", + "StatusPopupTitle": "Создание статуса и изменение имени существующего", + "NameAlreadyExists": "Данное имя уже исползутеся другим статусом этого типа" } } \ No newline at end of file diff --git a/plugins/task-resources/src/components/CreateStatePopup.svelte b/plugins/task-resources/src/components/CreateStatePopup.svelte new file mode 100644 index 0000000000..2f19f77710 --- /dev/null +++ b/plugins/task-resources/src/components/CreateStatePopup.svelte @@ -0,0 +1,87 @@ + + + + dispatch('close')} +> + + + {#if !canSave} + + diff --git a/plugins/task-resources/src/components/kanban/KanbanEditor.svelte b/plugins/task-resources/src/components/kanban/KanbanEditor.svelte index 941adc82fb..a91f31df26 100644 --- a/plugins/task-resources/src/components/kanban/KanbanEditor.svelte +++ b/plugins/task-resources/src/components/kanban/KanbanEditor.svelte @@ -14,7 +14,7 @@ // limitations under the License. --> - { - onAdd(e.detail) - }} - on:delete - on:move={onMove} -/> + diff --git a/plugins/task-resources/src/components/kanban/KanbanTemplateEditor.svelte b/plugins/task-resources/src/components/kanban/KanbanTemplateEditor.svelte index 6acec6f69e..cb3b823e0e 100644 --- a/plugins/task-resources/src/components/kanban/KanbanTemplateEditor.svelte +++ b/plugins/task-resources/src/components/kanban/KanbanTemplateEditor.svelte @@ -15,7 +15,7 @@ --> -{#if space?.editor} - +{#if spaceEditor} + {/if}
@@ -138,24 +143,28 @@ }} />
- + +
+ +
{ + showPopup( + StatusesPopup, + { + onDelete: () => dispatch('delete', { state }), + showDelete: states.length > 1, + onUpdate: () => { + showPopup(task.component.CreateStatePopup, { status: state, template }, undefined) + } + }, + eventToHTMLElement(ev), + () => {} + ) + }} + > +
- {#if states.length > 1} - -
{ - showPopup( - StatusesPopup, - { onDelete: () => dispatch('delete', { state }) }, - eventToHTMLElement(ev), - () => {} - ) - }} - > - -
- {/if} {/if} {/each} @@ -167,7 +176,15 @@ icon={IconAdd} size={'medium'} on:click={() => { - onAdd(task.class.WonState) + showPopup( + task.component.CreateStatePopup, + { + space, + template, + _class: template !== undefined ? task.class.WonStateTemplate : task.class.WonState + }, + undefined + ) }} /> @@ -185,24 +202,29 @@
- + + +
+ +
{ + showPopup( + StatusesPopup, + { + onDelete: () => dispatch('delete', { state }), + showDelete: wonStates.length > 1, + onUpdate: () => { + showPopup(task.component.CreateStatePopup, { status: state, template }, undefined) + } + }, + eventToHTMLElement(ev), + () => {} + ) + }} + > +
- {#if wonStates.length > 1} - -
{ - showPopup( - StatusesPopup, - { onDelete: () => dispatch('delete', { state }) }, - eventToHTMLElement(ev), - () => {} - ) - }} - > - -
- {/if} {/if} {/each} @@ -215,7 +237,15 @@ icon={IconAdd} size={'medium'} on:click={() => { - onAdd(task.class.LostState) + showPopup( + task.component.CreateStatePopup, + { + space, + template, + _class: template !== undefined ? task.class.LostStateTemplate : task.class.LostState + }, + undefined + ) }} /> @@ -233,24 +263,29 @@
- + + +
+ +
{ + showPopup( + StatusesPopup, + { + onDelete: () => dispatch('delete', { state }), + showDelete: lostStates.length > 1, + onUpdate: () => { + showPopup(task.component.CreateStatePopup, { status: state, template }, undefined) + } + }, + eventToHTMLElement(ev), + () => {} + ) + }} + > +
- {#if lostStates.length > 1} - -
{ - showPopup( - StatusesPopup, - { onDelete: () => dispatch('delete', { state }) }, - eventToHTMLElement(ev), - () => {} - ) - }} - > - -
- {/if} {/if} {/each} diff --git a/plugins/task-resources/src/components/state/StatusesPopup.svelte b/plugins/task-resources/src/components/state/StatusesPopup.svelte index 750e5e4706..882336a5de 100644 --- a/plugins/task-resources/src/components/state/StatusesPopup.svelte +++ b/plugins/task-resources/src/components/state/StatusesPopup.svelte @@ -14,28 +14,45 @@ -->
- + {#if showDelete} + +
{ + dispatch('close') + onDelete() + }} + > +
+ +
+
+ {/if}
{ dispatch('close') - onDelete() + onUpdate() }} >
- +
-
diff --git a/plugins/task-resources/src/index.ts b/plugins/task-resources/src/index.ts index 06c04cdf3f..00b32d2ed4 100644 --- a/plugins/task-resources/src/index.ts +++ b/plugins/task-resources/src/index.ts @@ -38,6 +38,7 @@ import Dashboard from './components/Dashboard.svelte' import DoneStateRefPresenter from './components/state/DoneStateRefPresenter.svelte' import StateRefPresenter from './components/state/StateRefPresenter.svelte' import DueDateEditor from './components/DueDateEditor.svelte' +import CreateStatePopup from './components/CreateStatePopup.svelte' export { default as AssigneePresenter } from './components/AssigneePresenter.svelte' export { StateRefPresenter } @@ -69,7 +70,8 @@ export default async (): Promise => ({ DoneStateRefPresenter, StateRefPresenter, TodoItemsPopup, - DueDateEditor + DueDateEditor, + CreateStatePopup }, actionImpl: { EditStatuses: editStatuses diff --git a/plugins/task-resources/src/plugin.ts b/plugins/task-resources/src/plugin.ts index b718f5ee25..9b3d274112 100644 --- a/plugins/task-resources/src/plugin.ts +++ b/plugins/task-resources/src/plugin.ts @@ -69,7 +69,10 @@ export default mergeIds(taskId, task, { Tasks: '' as IntlString, Task: '' as IntlString, - AllTime: '' as IntlString + AllTime: '' as IntlString, + StatusName: '' as IntlString, + StatusPopupTitle: '' as IntlString, + NameAlreadyExists: '' as IntlString }, status: { AssigneeRequired: '' as IntlString diff --git a/plugins/task/src/index.ts b/plugins/task/src/index.ts index f64f28aa5c..d49bbbe626 100644 --- a/plugins/task/src/index.ts +++ b/plugins/task/src/index.ts @@ -279,7 +279,8 @@ const task = plugin(taskId, { component: { KanbanTemplateEditor: '' as AnyComponent, KanbanTemplateSelector: '' as AnyComponent, - TodoItemsPopup: '' as AnyComponent + TodoItemsPopup: '' as AnyComponent, + CreateStatePopup: '' as AnyComponent }, ids: { AssigneedNotification: '' as Ref diff --git a/server-plugins/task-resources/src/index.ts b/server-plugins/task-resources/src/index.ts index ad6cba2f87..c52c0dda87 100644 --- a/server-plugins/task-resources/src/index.ts +++ b/server-plugins/task-resources/src/index.ts @@ -70,7 +70,7 @@ export async function OnTemplateStateCreate (tx: Tx, control: TriggerControl): P await control.findAll(task.class.KanbanTemplate, { _id: actualTx.attributes.attachedTo }) )[0] as KanbanTemplate const classToChange = getClassToChangeOrCreate(actualTx.objectClass) - const objectWithStatesToChange = await control.findAll(templateSpace.attachedToClass, { templateId: template._id }) + const objectWithStatesToChange = await control.findAll(templateSpace.attachedToClass, { templateId: template?._id }) const ids = Array.from(objectWithStatesToChange.map((x) => x._id)) as Array> const doc = TxProcessor.createDoc2Doc(actualTx) const ofAttribute = classToChange === task.class.State ? task.attribute.State : task.attribute.DoneState diff --git a/server/core/src/storage.ts b/server/core/src/storage.ts index 6b60217dde..77c670e082 100644 --- a/server/core/src/storage.ts +++ b/server/core/src/storage.ts @@ -196,7 +196,7 @@ class TServerStorage implements ServerStorage { return result[0] } if (result.length === 0) { - return [{}, false] + return false } return result } diff --git a/server/mongo/src/storage.ts b/server/mongo/src/storage.ts index f34fcbfcde..6679809239 100644 --- a/server/mongo/src/storage.ts +++ b/server/mongo/src/storage.ts @@ -737,7 +737,7 @@ class MongoAdapter extends MongoAdapterBase { return (await this.getOperations(txes[0])?.raw()) ?? {} } if (result.length === 0) { - return {} + return false } if (result.length === 1) { return result[0] diff --git a/tests/sanity/tests/settings.spec.ts b/tests/sanity/tests/settings.spec.ts index 8ff7f3262c..7c38312bb3 100644 --- a/tests/sanity/tests/settings.spec.ts +++ b/tests/sanity/tests/settings.spec.ts @@ -91,20 +91,18 @@ test.describe('contact tests', () => { await t.locator('input').fill(tid) // await page.locator(`#templates >> .container:has-text("${tid}")`).type('Enter') - // Click text=Active statuses >> div + await page.locator('.states >> svg >> nth=1').click() + await page.locator('text=Rename').click() + await page.locator('.box > .editbox-container input').fill('State1') + await page.locator('button:has-text("Save")').click() + await page.waitForSelector('form.antiCard', { state: 'detached' }) await page.click('text=Active statuses >> div') - - const s1 = page.locator('.states:has-text("New State")').first() - await s1.click() - await s1.locator('input').fill('State1') - + await page.locator('.box > .editbox-container input').fill('State2') + await page.locator('button:has-text("Save")').click() + await page.waitForSelector('form.antiCard', { state: 'detached' }) await page.click('text=Active statuses >> div') - const s2 = page.locator('.states:has-text("New State")').first() - await s2.click() - await s2.locator('input').fill('State2') - await page.click('text=Active statuses >> div') - const s3 = page.locator('.states:has-text("New State")').first() - await s3.click() - await s3.locator('input').fill('State3') + await page.locator('.box > .editbox-container input').fill('State3') + await page.locator('button:has-text("Save")').click() + await page.waitForSelector('form.antiCard', { state: 'detached' }) }) })