From 0466260734e1130a22b9490d89ca8cbc6c052c16 Mon Sep 17 00:00:00 2001 From: soujash-mandal <2002soujash@gmail.com> Date: Sat, 22 Jul 2023 16:51:22 +0530 Subject: [PATCH 01/11] navbar changes --- web/app/components/header/nav.hbs | 15 +++++++++------ web/app/styles/components/nav.scss | 3 +++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/web/app/components/header/nav.hbs b/web/app/components/header/nav.hbs index 0470b7c86..03a8d2857 100644 --- a/web/app/components/header/nav.hbs +++ b/web/app/components/header/nav.hbs @@ -38,16 +38,19 @@
diff --git a/web/app/styles/components/nav.scss b/web/app/styles/components/nav.scss index e2521b05d..afd458570 100644 --- a/web/app/styles/components/nav.scss +++ b/web/app/styles/components/nav.scss @@ -33,6 +33,9 @@ .user-buttons { @apply flex items-center justify-end space-x-1.5 sm:ml-6 md:ml-8 order-4; + .navbar-button{ + white-space: nowrap; + } } .user-avatar { From 1a7f7021ba25d75935f7224035a03510028d2ec7 Mon Sep 17 00:00:00 2001 From: soujash-mandal <2002soujash@gmail.com> Date: Sun, 23 Jul 2023 01:08:14 +0530 Subject: [PATCH 02/11] Due Date set due date can see due date in badge format of three colours 1. critical if due date is passed 2. warning if today is the due date 3. success in more than one days left one table column of due date is attached in the tables of drafts, all docs and my docs --- internal/api/documents.go | 11 +- internal/api/drafts.go | 5 +- pkg/hashicorpdocs/basedoc.go | 7 + pkg/models/document.go | 2 + .../components/dashboard/latest-updates.hbs | 1 + web/app/components/doc/row.hbs | 7 + web/app/components/doc/tile.hbs | 16 +- web/app/components/doc/tile.ts | 32 ++ web/app/components/document/sidebar.hbs | 41 ++ web/app/components/document/sidebar.ts | 9 +- web/app/components/row-results.hbs | 2 + web/app/helpers/dateformat.ts | 29 ++ web/app/templates/authenticated/dashboard.hbs | 396 ++++++++++-------- web/app/types/document.d.ts | 1 + 14 files changed, 365 insertions(+), 194 deletions(-) create mode 100644 web/app/helpers/dateformat.ts diff --git a/internal/api/documents.go b/internal/api/documents.go index e9cbf4a56..8a8c746e1 100644 --- a/internal/api/documents.go +++ b/internal/api/documents.go @@ -23,6 +23,7 @@ import ( // to be updated with a PATCH request. type DocumentPatchRequest struct { Reviewers []string `json:"reviewers,omitempty"` + DueDate string `json:"dueDate,omitempty"` Contributors []string `json:"contributors,omitempty"` Status string `json:"status,omitempty"` Summary string `json:"summary,omitempty"` @@ -212,17 +213,17 @@ func DocumentHandler( ) case "PATCH": - canPatchDocument:=true + canPatchDocument := true // Authorize request (only the owner can PATCH the doc). userEmail := r.Context().Value("userEmail").(string) for _, reviewer := range docObj.GetReviewers() { - if reviewer==userEmail { - canPatchDocument=false + if reviewer == userEmail { + canPatchDocument = false break } } - if userEmail==docObj.GetOwners()[0] { - canPatchDocument=false + if userEmail == docObj.GetOwners()[0] { + canPatchDocument = false } if canPatchDocument { diff --git a/internal/api/drafts.go b/internal/api/drafts.go index 255589761..0561b1a77 100644 --- a/internal/api/drafts.go +++ b/internal/api/drafts.go @@ -26,7 +26,8 @@ import ( ) type DraftsRequest struct { - Reviewers []string `json:"approvers,omitempty"` + Reviewers []string `json:"reviewers,omitempty"` + DueDate string `json:"dueDate,omitempty"` Contributors []string `json:"contributors,omitempty"` DocType string `json:"docType,omitempty"` Product string `json:"product,omitempty"` @@ -40,6 +41,7 @@ type DraftsRequest struct { // be updated with a PATCH request. type DraftsPatchRequest struct { Reviewers []string `json:"reviewers,omitempty"` + DueDate string `json:"dueDate,omitempty"` Contributors []string `json:"contributors,omitempty"` Product string `json:"product,omitempty"` Team string `json:"team,omitempty"` @@ -278,6 +280,7 @@ func DraftsHandler( d := models.Document{ GoogleFileID: f.Id, Reviewers: reviewers, + DueDate: req.DueDate, Contributors: contributors, DocumentCreatedAt: createdTime, DocumentModifiedAt: createdTime, diff --git a/pkg/hashicorpdocs/basedoc.go b/pkg/hashicorpdocs/basedoc.go index 6f0009acf..10b2eabd3 100644 --- a/pkg/hashicorpdocs/basedoc.go +++ b/pkg/hashicorpdocs/basedoc.go @@ -29,6 +29,9 @@ type BaseDoc struct { // are requested for the document. Reviewers []string `json:"reviewers,omitempty"` + // last date to review a doc for a reviewer + DueDate string `json:"dueDate,omitempty"` + // ChangesRequestedBy is a slice of email address strings for users that have // requested changes for the document. ChangesRequestedBy []string `json:"changesRequestedBy,omitempty"` @@ -108,6 +111,10 @@ func (d BaseDoc) GetReviewers() []string { return d.Reviewers } +func (d BaseDoc) GetDueDate() string { + return d.DueDate +} + func (d BaseDoc) GetChangesRequestedBy() []string { return d.ChangesRequestedBy } diff --git a/pkg/models/document.go b/pkg/models/document.go index ba0d1237c..266a0b273 100644 --- a/pkg/models/document.go +++ b/pkg/models/document.go @@ -23,6 +23,8 @@ type Document struct { // document. Reviewers []*User `gorm:"many2many:document_reviews;"` + // last date to review a doc for a reviewer + DueDate string // Contributors are users who have contributed to the document. Contributors []*User `gorm:"many2many:document_contributors;"` diff --git a/web/app/components/dashboard/latest-updates.hbs b/web/app/components/dashboard/latest-updates.hbs index 054ff87b4..aced9a167 100644 --- a/web/app/components/dashboard/latest-updates.hbs +++ b/web/app/components/dashboard/latest-updates.hbs @@ -45,6 +45,7 @@ @status={{lowercase doc.status}} @thumbnail={{doc.googleMetadata.thumbnailLink}} @title={{doc.title}} + @dueDate={{doc.dueDate}} /> {{/each}}
diff --git a/web/app/components/doc/row.hbs b/web/app/components/doc/row.hbs index 57ee3912d..f42448ad8 100644 --- a/web/app/components/doc/row.hbs +++ b/web/app/components/doc/row.hbs @@ -37,4 +37,11 @@ {{@createdDate}} + + {{#if (not (is-empty @dueDate))}} + {{dateformat @dueDate}} + {{else}} + No Due Date + {{/if}} + diff --git a/web/app/components/doc/tile.hbs b/web/app/components/doc/tile.hbs index 37c7a6f45..b9b07b05f 100644 --- a/web/app/components/doc/tile.hbs +++ b/web/app/components/doc/tile.hbs @@ -33,8 +33,22 @@ @text={{this.productAreaName}} @icon={{or (get-product-id this.this.args.productArea) "folder"}} /> + {{#if (not (is-empty @dueDate))}} + {{#if this.isDueDateOverdue}} + + {{else if this.isDueDateToday}} + + {{else}} + + {{/if}} + {{else}} + + {{/if}} {{#if (and @isResult @snippet)}} {{/if}} - + \ No newline at end of file diff --git a/web/app/components/doc/tile.ts b/web/app/components/doc/tile.ts index c9e75a7b9..77f7cccae 100644 --- a/web/app/components/doc/tile.ts +++ b/web/app/components/doc/tile.ts @@ -1,4 +1,5 @@ import Component from "@glimmer/component"; +import parseDate from "hermes/utils/parse-date"; interface DocTileComponentSignature { Args: { @@ -14,6 +15,7 @@ interface DocTileComponentSignature { status?: string; thumbnail?: string; title?: string; + dueDate?:string; }; } @@ -28,6 +30,36 @@ export default class DocTileComponent extends Componentdate) { + return true; + } + return false; + } + get isDueDateToday() : boolean { + if (this.currentDate==this.args.dueDate) { + return true; + } + return false; + } + + } declare module "@glint/environment-ember-loose/registry" { diff --git a/web/app/components/document/sidebar.hbs b/web/app/components/document/sidebar.hbs index 174534080..ad60d172f 100644 --- a/web/app/components/document/sidebar.hbs +++ b/web/app/components/document/sidebar.hbs @@ -279,6 +279,47 @@ {{/if}} +
+ Due Date + {{#if this.editingIsDisabled}} +

{{dateformat @document.dueDate}}

+ {{else}} + + <:default> + {{#unless (is-empty @document.dueDate)}} +

+ {{dateformat @document.dueDate}}

+ {{else}} +

+ Enter due date here

+ {{/unless}} + + <:editing as |F|> + + +
+ {{/if}} +
+
Created diff --git a/web/app/components/document/sidebar.ts b/web/app/components/document/sidebar.ts index 242724276..2b36e2b55 100644 --- a/web/app/components/document/sidebar.ts +++ b/web/app/components/document/sidebar.ts @@ -61,6 +61,7 @@ export default class DocumentSidebarComponent extends Component { + this.dueDate = date; + await this.save.perform("dueDate", this.dueDate); + // productAbbreviation is computed by the back end + }); + isAllReviewersReviewed( reviewedReviewers: string[], allReviewers: string[] diff --git a/web/app/components/row-results.hbs b/web/app/components/row-results.hbs index b6ed0e2e5..3618f6d02 100644 --- a/web/app/components/row-results.hbs +++ b/web/app/components/row-results.hbs @@ -22,6 +22,7 @@ Product/Area Owner Created + Due Date <:body> @@ -37,6 +38,7 @@ @status="{{lowercase doc.status}}" @title="{{doc.title}}" @isDraft={{@isDraft}} + @dueDate="{{doc.dueDate}}" /> {{/each}} diff --git a/web/app/helpers/dateformat.ts b/web/app/helpers/dateformat.ts new file mode 100644 index 000000000..c44c011cb --- /dev/null +++ b/web/app/helpers/dateformat.ts @@ -0,0 +1,29 @@ +// app/helpers/format-date.ts +import { helper } from '@ember/component/helper'; +const monthNames = [ + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + ]; +export function dateformat([date]: [string]): string { + let res="",year="",month="",day=""; + let n=date.length; + if (n===0) { + return year; + } + for (let index = 0; index <=3; index++) { + let element = date[index]; + year+=element; + } + for (let index = 5; index <=6; index++) { + let element = date[index]; + month+=element; + } + for (let index = 8; index <=9; index++) { + let element = date[index]; + day+=element; + } + res=`${monthNames[parseInt(month) - 1]} ${day}, ${year}` + return res; +} + +export default helper(dateformat); diff --git a/web/app/templates/authenticated/dashboard.hbs b/web/app/templates/authenticated/dashboard.hbs index e3ea77cce..1c101f8e2 100644 --- a/web/app/templates/authenticated/dashboard.hbs +++ b/web/app/templates/authenticated/dashboard.hbs @@ -1,236 +1,258 @@ {{page-title "Dashboard"}} -
-
+
+
-

Welcome back, {{this.authenticatedUser.info.given_name}} +

Welcome back, + {{this.authenticatedUser.info.given_name}} {{#if (eq this.authenticatedUser.info.role "Admin")}} - + {{/if}}

- - {{#if (eq this.authenticatedUser.info.role "Admin")}} -
- - + + + {{on "click" (fn this.toggleModal1)}} + /> - + {{on "click" (fn this.toggleModal2)}} + /> - + {{on "click" (fn this.toggleModal3)}} + /> + + +
+ {{/if}} -
-
- {{/if}} -

Here’s all the latest updates across the organization.

- {{#if this.showModal1}} - {{#modal-dialog - onClose=(action (action (mut this.showModal1) false)) - targetAttachment="center" - translucentOverlay=true - }} -
+
\ No newline at end of file diff --git a/web/app/types/document.d.ts b/web/app/types/document.d.ts index 62ef3f542..84f304a1e 100644 --- a/web/app/types/document.d.ts +++ b/web/app/types/document.d.ts @@ -20,6 +20,7 @@ export interface HermesDocument { appCreated?: boolean; contributors?: HermesUser[]; reviewers: HermesUser[]; + dueDate: string; changesRequestedBy?: string[]; reviewedBy: string[]; summary?: string; From 3846dd5304e7dbcfcefc3b07b18e7bc5d0a6d997 Mon Sep 17 00:00:00 2001 From: soujash-mandal <2002soujash@gmail.com> Date: Sun, 23 Jul 2023 02:12:51 +0530 Subject: [PATCH 03/11] sort according to due date --- web/app/routes/authenticated/dashboard.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/web/app/routes/authenticated/dashboard.js b/web/app/routes/authenticated/dashboard.js index 25fb3abeb..c82f61815 100644 --- a/web/app/routes/authenticated/dashboard.js +++ b/web/app/routes/authenticated/dashboard.js @@ -2,7 +2,7 @@ import Route from "@ember/routing/route"; import RSVP from "rsvp"; import { inject as service } from "@ember/service"; import timeAgo from "hermes/utils/time-ago"; -import {action} from "@ember/object"; +import { action } from "@ember/object"; export default class DashboardRoute extends Route { @service algolia; @@ -44,7 +44,7 @@ export default class DashboardRoute extends Route { ` AND NOT reviewedBy:'${userInfo.email}'` + " AND appCreated:true" + " AND status:In-Review", - hitsPerPage: 4, + hitsPerPage: 1000, }) .then((result) => { // Add modifiedAgo for each doc. @@ -79,7 +79,9 @@ export default class DashboardRoute extends Route { this.recentDocs.all = null; } } - + docsWaitingForReview._result = docsWaitingForReview._result.sort((a, b) => + a.dueDate.localeCompare(b.dueDate) + ); return RSVP.hash({ docsWaitingForReview: docsWaitingForReview, }); @@ -104,5 +106,4 @@ export default class DashboardRoute extends Route { } return parentsQuery; } - } From e07dbf35d7ff93ef79aa33c0437a68228b90ec16 Mon Sep 17 00:00:00 2001 From: soujash-mandal <2002soujash@gmail.com> Date: Sun, 23 Jul 2023 05:43:08 +0530 Subject: [PATCH 04/11] date formatted to MMM DD, YYYY --- web/app/components/row-results.hbs | 3 ++- web/app/routes/authenticated/document.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/web/app/components/row-results.hbs b/web/app/components/row-results.hbs index 3618f6d02..3560f0b04 100644 --- a/web/app/components/row-results.hbs +++ b/web/app/components/row-results.hbs @@ -29,7 +29,8 @@ {{#each @docs as |doc index|}} Date: Sun, 23 Jul 2023 16:52:26 +0530 Subject: [PATCH 05/11] Colours for badge only allowed for docs waiting for me to review --- .../components/dashboard/latest-updates.hbs | 1 + web/app/components/doc/tile.hbs | 26 +++++++++++++------ web/app/components/doc/tile.ts | 1 + web/app/templates/authenticated/dashboard.hbs | 2 ++ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/web/app/components/dashboard/latest-updates.hbs b/web/app/components/dashboard/latest-updates.hbs index aced9a167..92fd01e76 100644 --- a/web/app/components/dashboard/latest-updates.hbs +++ b/web/app/components/dashboard/latest-updates.hbs @@ -46,6 +46,7 @@ @thumbnail={{doc.googleMetadata.thumbnailLink}} @title={{doc.title}} @dueDate={{doc.dueDate}} + @showColorBadge={{false}} /> {{/each}}
diff --git a/web/app/components/doc/tile.hbs b/web/app/components/doc/tile.hbs index b9b07b05f..c799d1197 100644 --- a/web/app/components/doc/tile.hbs +++ b/web/app/components/doc/tile.hbs @@ -34,15 +34,25 @@ @icon={{or (get-product-id this.this.args.productArea) "folder"}} /> {{#if (not (is-empty @dueDate))}} - {{#if this.isDueDateOverdue}} - - {{else if this.isDueDateToday}} - + {{#if @showColorBadge}} + {{#if this.isDueDateOverdue}} + + {{else if this.isDueDateToday}} + + {{else}} + + {{/if}} {{else}} - + {{/if}} {{else}} diff --git a/web/app/components/doc/tile.ts b/web/app/components/doc/tile.ts index 77f7cccae..5361e5d9e 100644 --- a/web/app/components/doc/tile.ts +++ b/web/app/components/doc/tile.ts @@ -16,6 +16,7 @@ interface DocTileComponentSignature { thumbnail?: string; title?: string; dueDate?:string; + showColorBadge:boolean; }; } diff --git a/web/app/templates/authenticated/dashboard.hbs b/web/app/templates/authenticated/dashboard.hbs index 1c101f8e2..e4313acc2 100644 --- a/web/app/templates/authenticated/dashboard.hbs +++ b/web/app/templates/authenticated/dashboard.hbs @@ -278,6 +278,7 @@ @status="{{lowercase doc.status}}" @title="{{doc.title}}" @dueDate="{{doc.dueDate}}" + @showColorBadge={{true}} /> {{/if}} {{/each}} @@ -308,6 +309,7 @@ @status={{lowercase r.doc.status}} @title={{r.doc.title}} @dueDate={{r.doc.dueDate}} + @showColorBadge={{false}} /> {{/each}} From e1db7e852d61e625103042bca58c4ee3b8d463b5 Mon Sep 17 00:00:00 2001 From: soujash-mandal <2002soujash@gmail.com> Date: Mon, 24 Jul 2023 20:29:02 +0530 Subject: [PATCH 06/11] Docs Waiting for me and Reviewed By me --- web/app/components/doc/row.hbs | 29 +++- web/app/components/doc/row.ts | 24 +++ web/app/components/doc/tile.hbs | 6 +- web/app/components/header/nav.hbs | 76 ++++----- .../authenticated/waiting-for-me.js | 24 +++ web/app/router.js | 1 + .../routes/authenticated/waiting-for-me.js | 144 ++++++++++++++++++ web/app/styles/app.scss | 3 + .../authenticated/waiting-for-me.hbs | 126 +++++++++++++++ 9 files changed, 388 insertions(+), 45 deletions(-) create mode 100644 web/app/controllers/authenticated/waiting-for-me.js create mode 100644 web/app/routes/authenticated/waiting-for-me.js create mode 100644 web/app/templates/authenticated/waiting-for-me.hbs diff --git a/web/app/components/doc/row.hbs b/web/app/components/doc/row.hbs index f42448ad8..3631b4f6e 100644 --- a/web/app/components/doc/row.hbs +++ b/web/app/components/doc/row.hbs @@ -6,9 +6,9 @@ @query={{hash draft=@isDraft}} class="flex space-x-4 items-start" > -
@@ -39,9 +39,24 @@ {{@createdDate}} {{#if (not (is-empty @dueDate))}} - {{dateformat @dueDate}} + {{#if @showColorBadge}} + {{#if @isReviewed}} + + {{else if this.isDueDateOverdue}} + + {{else}} + + {{/if}} + {{else}} + + {{/if}} {{else}} - No Due Date + {{#if @isReviewed}} + + {{else}} + + {{/if}} + {{/if}} - - + + \ No newline at end of file diff --git a/web/app/components/doc/row.ts b/web/app/components/doc/row.ts index c6d681e83..6079f244e 100644 --- a/web/app/components/doc/row.ts +++ b/web/app/components/doc/row.ts @@ -1,4 +1,5 @@ import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; interface DocRowComponentSignature { Args: { @@ -14,6 +15,8 @@ interface DocRowComponentSignature { status: string; isResult?: boolean; isOwner?: boolean; + showColorBadge?:boolean; + dueDate?:string; }; } @@ -25,6 +28,27 @@ export default class DocRowComponent extends Component return this.args.productArea; } } + get currentDate() { + // Get the current date + const today = new Date(); + + // Extract year, month, and day from the current date + const year = today.getFullYear(); + const month = String(today.getMonth() + 1).padStart(2, '0'); // Months are zero-based, so we add 1 + const day = String(today.getDate()).padStart(2, '0'); + + // Concatenate the parts in the desired format "yyyy-mm-dd" + return `${year}-${month}-${day}`; + } + + + get isDueDateOverdue() : boolean { + let date =this.args.dueDate||""; + if (this.currentDate>date) { + return true; + } + return false; + } } declare module "@glint/environment-ember-loose/registry" { diff --git a/web/app/components/doc/tile.hbs b/web/app/components/doc/tile.hbs index c799d1197..618aad9c6 100644 --- a/web/app/components/doc/tile.hbs +++ b/web/app/components/doc/tile.hbs @@ -40,16 +40,16 @@ @color="critical" @text="Due Date : {{dateformat @dueDate}}" /> - {{else if this.isDueDateToday}} + {{else}} - {{else}} + {{!-- {{else}} + /> --}} {{/if}} {{else}} diff --git a/web/app/components/header/nav.hbs b/web/app/components/header/nav.hbs index 03a8d2857..8d5e36fc3 100644 --- a/web/app/components/header/nav.hbs +++ b/web/app/components/header/nav.hbs @@ -1,5 +1,5 @@ {{! @glint-nocheck: not typesafe yet }} -
+
@@ -40,19 +48,19 @@ @route="authenticated.new" {{!-- @isIconOnly={{true}} --}} @text="New Doc" - {{!-- @icon="file-plus" --}} - {{!-- class="create-draft-button" --}} + @icon="file-plus" + {{! class="create-draft-button" }} class="navbar-button" /> + @icon ="folder-users" + {{!-- @isIconOnly={{true}} --}} + @text="Directory" + @color="secondary" + class="navbar-button" + {{on "click" (fn this.toggleModal)}} + />
{{#if this.userMenuHighlightIsShown}} @@ -97,7 +105,7 @@ /> @@ -114,32 +122,30 @@
- - - {{#if this.showModal}} - - -

Directory List

-
- -

-

    - {{#each-in this.products as |buKey buValue|}} - {{!-- Use the 'collapsible-folder' component --}} - - {{/each-in}} -
-

-
- +{{#if this.showModal}} + + +

Directory List

+
+ +

+

    + {{#each-in this.products as |buKey buValue|}} + {{! Use the 'collapsible-folder' component }} + + {{/each-in}} +
+

+
+ - -
- {{/if}} \ No newline at end of file +
+{{/if}} \ No newline at end of file diff --git a/web/app/controllers/authenticated/waiting-for-me.js b/web/app/controllers/authenticated/waiting-for-me.js new file mode 100644 index 000000000..8de43f56e --- /dev/null +++ b/web/app/controllers/authenticated/waiting-for-me.js @@ -0,0 +1,24 @@ +import Controller from "@ember/controller"; +import { alias } from "@ember/object/computed"; +import { inject as service } from "@ember/service"; +import {tracked} from "@glimmer/tracking"; +import {action} from "@ember/object"; +import {task, timeout} from "ember-concurrency"; +import Ember from "ember"; +import {TaskForAsyncTaskFunction} from "ember-concurrency"; +import FetchService from "../../services/fetch"; +import { HermesUser } from "hermes/types/document"; + +const AWAIT_DOC_DELAY = Ember.testing ? 0 : 1000; + +export default class AuthenticatedWaitingForMeController extends Controller { + @alias("model.docsWaitingForReview") docsWaitingForReview; + @alias("model.docsReviewed") docsReviewed; + + @service router; + @service authenticatedUser; + @service("config") configSvc; + @service("recently-viewed-docs") recentDocs; + @service('flash-messages') flashMessages; + @service("fetch") fetchSvc: FetchService; +} \ No newline at end of file diff --git a/web/app/router.js b/web/app/router.js index 8be2cced2..cb929fc39 100644 --- a/web/app/router.js +++ b/web/app/router.js @@ -13,6 +13,7 @@ Router.map(function () { this.route("document", { path: "/document/:document_id" }); this.route("drafts"); this.route("my"); + this.route("waiting-for-me"); this.route("results"); this.route("settings"); this.route("new", function () { diff --git a/web/app/routes/authenticated/waiting-for-me.js b/web/app/routes/authenticated/waiting-for-me.js new file mode 100644 index 000000000..57cd55e2d --- /dev/null +++ b/web/app/routes/authenticated/waiting-for-me.js @@ -0,0 +1,144 @@ +import Route from "@ember/routing/route"; +import RSVP from "rsvp"; +import { inject as service } from "@ember/service"; +import timeAgo from "hermes/utils/time-ago"; +import { action } from "@ember/object"; + +export default class WaitingForMeRoute extends Route { + @service algolia; + @service("config") configSvc; + @service("fetch") fetchSvc; + @service("recently-viewed-docs") recentDocs; + @service session; + @service authenticatedUser; + + queryParams = { + latestUpdates: { + refreshModel: true, + replace: true, + }, + }; + + resetController(controller, isExiting, transition) { + if (isExiting) { + controller.set("latestUpdates", "newDocs"); + } + } + + async model(params) { + // Create facet filter for recently updated docs depending on the selected + // "Latest updates" tab. + let facetFilter = ""; + if (params.latestUpdates == "reviewed") { + facetFilter = "status:reviewed"; + } else if (params.latestUpdates == "inReview") { + facetFilter = "status:In-Review"; + } + + const userInfo = this.authenticatedUser.info; + + const docsWaitingForReview = this.algolia.searchIndex + .perform(this.configSvc.config.algolia_docs_index_name, "", { + filters: + `reviewers:'${userInfo.email}'` + + ` AND NOT reviewedBy:'${userInfo.email}'` + + " AND appCreated:true" + + " AND status:In-Review", + hitsPerPage: 1000, + }) + .then((result) => { + // Add modifiedAgo for each doc. + for (const hit of result.hits) { + this.fetchSvc + .fetch("/api/v1/documents/" + hit.objectID) + .then((resp) => resp.json()) + .then((doc) => { + if (doc.modifiedTime) { + const modifiedDate = new Date(doc.modifiedTime * 1000); + hit.modifiedAgo = `Modified ${timeAgo(modifiedDate)}`; + } + }) + .catch((err) => { + console.log( + `Error getting document waiting for review (${hit.objectID}):`, + err + ); + }); + } + return result.hits; + }); + + const docsReviewed = this.algolia.searchIndex + .perform(this.configSvc.config.algolia_docs_index_name, "", { + filters: + `reviewers:'${userInfo.email}'` + + ` AND reviewedBy:'${userInfo.email}'` + + " AND appCreated:true", + // + + // " AND status:In-Review", + hitsPerPage: 1000, + }) + .then((result) => { + // Add modifiedAgo for each doc. + for (const hit of result.hits) { + this.fetchSvc + .fetch("/api/v1/documents/" + hit.objectID) + .then((resp) => resp.json()) + .then((doc) => { + if (doc.modifiedTime) { + const modifiedDate = new Date(doc.modifiedTime * 1000); + hit.modifiedAgo = `Modified ${timeAgo(modifiedDate)}`; + } + }) + .catch((err) => { + console.log( + `Error getting document waiting for review (${hit.objectID}):`, + err + ); + }); + } + return result.hits; + }); + + await this.recentDocs.fetchAll.perform(); + if (this.recentDocs.all === null) { + try { + await this.recentDocs.fetchAll.perform(); + } catch { + /** + * This tells our template to show the error state. + */ + this.recentDocs.all = null; + } + } + + docsWaitingForReview._result = docsWaitingForReview._result.sort((a, b) => + a.dueDate.localeCompare(b.dueDate) + ); + console.log("docsReviewed : ", docsReviewed); + return RSVP.hash({ + docsWaitingForReview: docsWaitingForReview, + docsReviewed: docsReviewed, + }); + } + + /** + * Builds a parent query string for searching for Google files. The folders + * parameter is an array of all folder ID strings to search. + */ + buildParentsQuery(folders) { + let parentsQuery = ""; + if (folders.length > 0) { + parentsQuery += " and ("; + folders.forEach((folder, index) => { + if (index == 0) { + parentsQuery += `'${folder}' in parents`; + } else { + parentsQuery += ` or '${folder}' in parents`; + } + }); + parentsQuery += ")"; + } + return parentsQuery; + } +} diff --git a/web/app/styles/app.scss b/web/app/styles/app.scss index 5050c350e..2a8991985 100644 --- a/web/app/styles/app.scss +++ b/web/app/styles/app.scss @@ -254,6 +254,9 @@ ol { .x-container { @apply w-full max-w-screen-lg mx-auto px-8; } +.x-container-navbar { + @apply w-full mx-auto px-20; +} h1 { @apply text-display-500 font-bold text-color-foreground-strong mb-1.5; diff --git a/web/app/templates/authenticated/waiting-for-me.hbs b/web/app/templates/authenticated/waiting-for-me.hbs new file mode 100644 index 000000000..8b6b4209d --- /dev/null +++ b/web/app/templates/authenticated/waiting-for-me.hbs @@ -0,0 +1,126 @@ +{{page-title "waiting for me"}} +
+
+ {{#if this.docsWaitingForReview}} +
+
+ +

Documents waiting for your review

+
+
+ {{!--
+ {{#each this.docsWaitingForReview as |doc index|}} + {{#if (lt index 1000)}} + + {{/if}} + {{/each}} +
--}} + + <:head as |H|> + + Name + Type + Status + Product/Area + Owner + Created + Due Date + + + <:body> + {{#each this.docsWaitingForReview as |doc index|}} + + {{/each}} + + +
+ {{/if}} +
+
+ +

Documents you have reviewed

+
+
+ {{!--
+ {{#each this.docsWaitingForReview as |doc index|}} + {{#if (lt index 1000)}} + + {{/if}} + {{/each}} +
--}} + + <:head as |H|> + + Name + Type + Status + Product/Area + Owner + Created + Due Date + + + <:body> + {{#each this.docsReviewed as |doc index|}} + + {{/each}} + + +
+ +
\ No newline at end of file From 8e9358eb841f12965314742d8eff2d256a453432 Mon Sep 17 00:00:00 2001 From: soujash-mandal <2002soujash@gmail.com> Date: Tue, 25 Jul 2023 00:02:07 +0530 Subject: [PATCH 07/11] Merge Conflict Bug Fix --- web/app/components/doc/row.hbs | 22 ++++++++++++++++++++++ web/app/components/doc/tile.hbs | 20 ++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/web/app/components/doc/row.hbs b/web/app/components/doc/row.hbs index e46135904..3631b4f6e 100644 --- a/web/app/components/doc/row.hbs +++ b/web/app/components/doc/row.hbs @@ -37,4 +37,26 @@ {{@createdDate}} + + {{#if (not (is-empty @dueDate))}} + {{#if @showColorBadge}} + {{#if @isReviewed}} + + {{else if this.isDueDateOverdue}} + + {{else}} + + {{/if}} + {{else}} + + {{/if}} + {{else}} + {{#if @isReviewed}} + + {{else}} + + {{/if}} + + {{/if}} + \ No newline at end of file diff --git a/web/app/components/doc/tile.hbs b/web/app/components/doc/tile.hbs index 924113e75..ca29968b4 100644 --- a/web/app/components/doc/tile.hbs +++ b/web/app/components/doc/tile.hbs @@ -34,6 +34,26 @@ @icon={{or (get-product-id this.this.args.productArea) "folder"}} /> + {{#if (not (is-empty @dueDate))}} + {{#if @showColorBadge}} + {{#if this.isDueDateOverdue}} + + {{else}} + + {{/if}} + {{else}} + + {{/if}} + {{else}} + + {{/if}} + {{#if (and @isResult @snippet)}} {{/if}} From cfcd957e79d664a9ae3970c1e8af9accc2769de9 Mon Sep 17 00:00:00 2001 From: soujash-mandal <2002soujash@gmail.com> Date: Tue, 25 Jul 2023 01:51:47 +0530 Subject: [PATCH 08/11] fixing sort wrt due Date bug --- web/app/routes/authenticated/dashboard.js | 18 ++++++++++++++--- .../routes/authenticated/waiting-for-me.js | 20 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/web/app/routes/authenticated/dashboard.js b/web/app/routes/authenticated/dashboard.js index c82f61815..3c2aa56de 100644 --- a/web/app/routes/authenticated/dashboard.js +++ b/web/app/routes/authenticated/dashboard.js @@ -79,9 +79,21 @@ export default class DashboardRoute extends Route { this.recentDocs.all = null; } } - docsWaitingForReview._result = docsWaitingForReview._result.sort((a, b) => - a.dueDate.localeCompare(b.dueDate) - ); + docsWaitingForReview._result = docsWaitingForReview._result.sort((a, b) => { + // Check if 'dueDate' property exists in both 'a' and 'b' + if (a.dueDate && b.dueDate) { + return a.dueDate.localeCompare(b.dueDate); + } else if (a.dueDate) { + // If 'dueDate' exists in 'a' but not in 'b', consider 'a' to come before 'b' + return -1; + } else if (b.dueDate) { + // If 'dueDate' exists in 'b' but not in 'a', consider 'b' to come before 'a' + return 1; + } else { + // If 'dueDate' doesn't exist in both 'a' and 'b', maintain their original order + return 0; + } + }); return RSVP.hash({ docsWaitingForReview: docsWaitingForReview, }); diff --git a/web/app/routes/authenticated/waiting-for-me.js b/web/app/routes/authenticated/waiting-for-me.js index 57cd55e2d..c811eb4d6 100644 --- a/web/app/routes/authenticated/waiting-for-me.js +++ b/web/app/routes/authenticated/waiting-for-me.js @@ -112,10 +112,22 @@ export default class WaitingForMeRoute extends Route { } } - docsWaitingForReview._result = docsWaitingForReview._result.sort((a, b) => - a.dueDate.localeCompare(b.dueDate) - ); - console.log("docsReviewed : ", docsReviewed); + docsWaitingForReview._result = docsWaitingForReview._result.sort((a, b) => { + // Check if 'dueDate' property exists in both 'a' and 'b' + if (a.dueDate && b.dueDate) { + return a.dueDate.localeCompare(b.dueDate); + } else if (a.dueDate) { + // If 'dueDate' exists in 'a' but not in 'b', consider 'a' to come before 'b' + return -1; + } else if (b.dueDate) { + // If 'dueDate' exists in 'b' but not in 'a', consider 'b' to come before 'a' + return 1; + } else { + // If 'dueDate' doesn't exist in both 'a' and 'b', maintain their original order + return 0; + } + }); + return RSVP.hash({ docsWaitingForReview: docsWaitingForReview, docsReviewed: docsReviewed, From c434646e7f1186f4fcd7e1ff6c0a01cfc0a89338 Mon Sep 17 00:00:00 2001 From: soujash-mandal <2002soujash@gmail.com> Date: Tue, 25 Jul 2023 10:48:57 +0530 Subject: [PATCH 09/11] resolving sorting error --- web/app/routes/authenticated/dashboard.js | 16 ++++++++++++---- web/app/routes/authenticated/waiting-for-me.js | 14 +++++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/web/app/routes/authenticated/dashboard.js b/web/app/routes/authenticated/dashboard.js index 3c2aa56de..3c0d1da02 100644 --- a/web/app/routes/authenticated/dashboard.js +++ b/web/app/routes/authenticated/dashboard.js @@ -79,14 +79,20 @@ export default class DashboardRoute extends Route { this.recentDocs.all = null; } } + + docsWaitingForReview._result = docsWaitingForReview._result.sort((a, b) => { + // Use optional chaining to access the 'dueDate' property safely + const dueDateA = a.dueDate?.toString()||""; + const dueDateB = b.dueDate?.toString()||""; + // Check if 'dueDate' property exists in both 'a' and 'b' - if (a.dueDate && b.dueDate) { - return a.dueDate.localeCompare(b.dueDate); - } else if (a.dueDate) { + if (dueDateA && dueDateB) { + return dueDateA.localeCompare(dueDateB); + } else if (dueDateA) { // If 'dueDate' exists in 'a' but not in 'b', consider 'a' to come before 'b' return -1; - } else if (b.dueDate) { + } else if (dueDateB) { // If 'dueDate' exists in 'b' but not in 'a', consider 'b' to come before 'a' return 1; } else { @@ -94,6 +100,8 @@ export default class DashboardRoute extends Route { return 0; } }); + + return RSVP.hash({ docsWaitingForReview: docsWaitingForReview, }); diff --git a/web/app/routes/authenticated/waiting-for-me.js b/web/app/routes/authenticated/waiting-for-me.js index c811eb4d6..7b0264edc 100644 --- a/web/app/routes/authenticated/waiting-for-me.js +++ b/web/app/routes/authenticated/waiting-for-me.js @@ -113,13 +113,17 @@ export default class WaitingForMeRoute extends Route { } docsWaitingForReview._result = docsWaitingForReview._result.sort((a, b) => { + // Use optional chaining to access the 'dueDate' property safely + const dueDateA = a.dueDate?.toString()||""; + const dueDateB = b.dueDate?.toString()||""; + // Check if 'dueDate' property exists in both 'a' and 'b' - if (a.dueDate && b.dueDate) { - return a.dueDate.localeCompare(b.dueDate); - } else if (a.dueDate) { + if (dueDateA && dueDateB) { + return dueDateA.localeCompare(dueDateB); + } else if (dueDateA) { // If 'dueDate' exists in 'a' but not in 'b', consider 'a' to come before 'b' return -1; - } else if (b.dueDate) { + } else if (dueDateB) { // If 'dueDate' exists in 'b' but not in 'a', consider 'b' to come before 'a' return 1; } else { @@ -127,7 +131,7 @@ export default class WaitingForMeRoute extends Route { return 0; } }); - + return RSVP.hash({ docsWaitingForReview: docsWaitingForReview, docsReviewed: docsReviewed, From 783d762206d4528322d0223f1d6081701352a889 Mon Sep 17 00:00:00 2001 From: soujash-mandal <2002soujash@gmail.com> Date: Tue, 25 Jul 2023 17:17:18 +0530 Subject: [PATCH 10/11] sort the waiting docs wrt due date resolved frontend-> algolia --- pkg/algolia/client.go | 94 ++++++++++++++++++- web/app/routes/authenticated/dashboard.js | 25 +---- .../routes/authenticated/waiting-for-me.js | 29 +----- 3 files changed, 98 insertions(+), 50 deletions(-) diff --git a/pkg/algolia/client.go b/pkg/algolia/client.go index 73c3d33ab..e210b718b 100644 --- a/pkg/algolia/client.go +++ b/pkg/algolia/client.go @@ -28,6 +28,8 @@ type Client struct { // by descending modified time. DocsModifiedTimeDesc *search.Index + DocsDueDateAsc *search.Index + // Drafts is an Algolia index for storing metadata for draft documents. Drafts *search.Index @@ -143,6 +145,7 @@ func New(cfg *Config) (*Client, error) { cfg.DocsIndexName+"_createdTime_asc", cfg.DocsIndexName+"_createdTime_desc", cfg.DocsIndexName+"_modifiedTime_desc", + cfg.DocsIndexName+"_dueDate_asc", ), }) if err != nil { @@ -153,11 +156,14 @@ func New(cfg *Config) (*Client, error) { c.DocsCreatedTimeAsc = a.InitIndex(cfg.DocsIndexName + "_createdTime_asc") c.DocsCreatedTimeDesc = a.InitIndex(cfg.DocsIndexName + "_createdTime_desc") c.DocsModifiedTimeDesc = a.InitIndex(cfg.DocsIndexName + "_modifiedTime_desc") - err = configureReplicaIndexes( + c.DocsDueDateAsc = a.InitIndex(cfg.DocsIndexName + "_dueDate_asc") + + err = configureDocsReplicaIndexes( cfg.DocsIndexName, c.DocsCreatedTimeAsc, c.DocsCreatedTimeDesc, c.DocsModifiedTimeDesc, + c.DocsDueDateAsc, ) if err != nil { return nil, err @@ -224,6 +230,7 @@ func configureReplicaIndexes( createdTimeAscIndex *search.Index, createdTimeDescIndex *search.Index, modifiedTimeDescIndex *search.Index, + ) error { // Configure the createdTime_asc replica for index. _, err := createdTimeAscIndex.SetSettings(search.Settings{ @@ -284,6 +291,90 @@ func configureReplicaIndexes( return nil } +func configureDocsReplicaIndexes( + indexName string, + createdTimeAscIndex *search.Index, + createdTimeDescIndex *search.Index, + modifiedTimeDescIndex *search.Index, + dueDateAscIndex *search.Index, +) error { + // Configure the createdTime_asc replica for index. + _, err := createdTimeAscIndex.SetSettings(search.Settings{ + AttributesForFaceting: opt.AttributesForFaceting( + "contributors", + "docType", + "owners", + "product", + "status", + ), + + Ranking: opt.Ranking( + "asc(createdTime)", + ), + }) + if err != nil { + return fmt.Errorf( + "error setting settings for the %s createdTime_asc standard replica: %w", + indexName, err) + } + + // Configure the createdTime_desc replica for index. + _, err = createdTimeDescIndex.SetSettings(search.Settings{ + AttributesForFaceting: opt.AttributesForFaceting( + "contributors", + "docType", + "owners", + "product", + "status", + ), + + Ranking: opt.Ranking( + "desc(createdTime)", + ), + }) + if err != nil { + return fmt.Errorf( + "error setting settings for the %s createdTime_desc standard replica: %w", + indexName, err) + } + + // Configure the modifiedTime_desc replica for index. + _, err = modifiedTimeDescIndex.SetSettings(search.Settings{ + AttributesForFaceting: opt.AttributesForFaceting( + "status", + ), + + Ranking: opt.Ranking( + "desc(modifiedTime)", + ), + }) + if err != nil { + return fmt.Errorf( + "error setting settings for the %s modifiedTime_desc standard replica: %w", + indexName, err) + } + + // Configure the dueDate_asc replica for index. + _, err = dueDateAscIndex.SetSettings(search.Settings{ + AttributesForFaceting: opt.AttributesForFaceting( + "reviewers", + "reviewedBy", + "appCreated", + "status", + ), + Ranking: opt.Ranking( + "asc(dueDate)", + ), + }) + if err != nil { + return fmt.Errorf( + "error setting settings for the %s modifiedTime_desc standard replica: %w", + indexName, err) + } + + return nil +} + // NewSearchClient returns a new Algolia client for searching indices. func NewSearchClient(cfg *Config) (*Client, error) { if err := validate(cfg); err != nil { @@ -299,6 +390,7 @@ func NewSearchClient(cfg *Config) (*Client, error) { c.DocsCreatedTimeAsc = a.InitIndex(cfg.DocsIndexName + "_createdTime_asc") c.DocsCreatedTimeDesc = a.InitIndex(cfg.DocsIndexName + "_createdTime_desc") c.DocsModifiedTimeDesc = a.InitIndex(cfg.DocsIndexName + "_modifiedTime_desc") + c.DocsDueDateAsc = a.InitIndex(cfg.DocsIndexName + "_dueDate_asc") c.Drafts = a.InitIndex(cfg.DraftsIndexName) c.DraftsCreatedTimeAsc = a.InitIndex(cfg.DraftsIndexName + "_createdTime_asc") c.DraftsCreatedTimeDesc = a.InitIndex(cfg.DraftsIndexName + "_createdTime_desc") diff --git a/web/app/routes/authenticated/dashboard.js b/web/app/routes/authenticated/dashboard.js index 3c0d1da02..8f6748dbd 100644 --- a/web/app/routes/authenticated/dashboard.js +++ b/web/app/routes/authenticated/dashboard.js @@ -38,13 +38,13 @@ export default class DashboardRoute extends Route { const userInfo = this.authenticatedUser.info; const docsWaitingForReview = this.algolia.searchIndex - .perform(this.configSvc.config.algolia_docs_index_name, "", { + .perform(this.configSvc.config.algolia_docs_index_name+"_dueDate_asc","", { filters: `reviewers:'${userInfo.email}'` + ` AND NOT reviewedBy:'${userInfo.email}'` + " AND appCreated:true" + " AND status:In-Review", - hitsPerPage: 1000, + hitsPerPage: 4, }) .then((result) => { // Add modifiedAgo for each doc. @@ -81,27 +81,6 @@ export default class DashboardRoute extends Route { } - docsWaitingForReview._result = docsWaitingForReview._result.sort((a, b) => { - // Use optional chaining to access the 'dueDate' property safely - const dueDateA = a.dueDate?.toString()||""; - const dueDateB = b.dueDate?.toString()||""; - - // Check if 'dueDate' property exists in both 'a' and 'b' - if (dueDateA && dueDateB) { - return dueDateA.localeCompare(dueDateB); - } else if (dueDateA) { - // If 'dueDate' exists in 'a' but not in 'b', consider 'a' to come before 'b' - return -1; - } else if (dueDateB) { - // If 'dueDate' exists in 'b' but not in 'a', consider 'b' to come before 'a' - return 1; - } else { - // If 'dueDate' doesn't exist in both 'a' and 'b', maintain their original order - return 0; - } - }); - - return RSVP.hash({ docsWaitingForReview: docsWaitingForReview, }); diff --git a/web/app/routes/authenticated/waiting-for-me.js b/web/app/routes/authenticated/waiting-for-me.js index 7b0264edc..c8e7a12af 100644 --- a/web/app/routes/authenticated/waiting-for-me.js +++ b/web/app/routes/authenticated/waiting-for-me.js @@ -36,9 +36,9 @@ export default class WaitingForMeRoute extends Route { } const userInfo = this.authenticatedUser.info; - + const searchIndex=this.configSvc.config.algolia_docs_index_name + "_dueDate_asc"; const docsWaitingForReview = this.algolia.searchIndex - .perform(this.configSvc.config.algolia_docs_index_name, "", { + .perform(searchIndex, "", { filters: `reviewers:'${userInfo.email}'` + ` AND NOT reviewedBy:'${userInfo.email}'` + @@ -69,13 +69,11 @@ export default class WaitingForMeRoute extends Route { }); const docsReviewed = this.algolia.searchIndex - .perform(this.configSvc.config.algolia_docs_index_name, "", { + .perform(searchIndex, "", { filters: `reviewers:'${userInfo.email}'` + ` AND reviewedBy:'${userInfo.email}'` + " AND appCreated:true", - // + - // " AND status:In-Review", hitsPerPage: 1000, }) .then((result) => { @@ -111,27 +109,6 @@ export default class WaitingForMeRoute extends Route { this.recentDocs.all = null; } } - - docsWaitingForReview._result = docsWaitingForReview._result.sort((a, b) => { - // Use optional chaining to access the 'dueDate' property safely - const dueDateA = a.dueDate?.toString()||""; - const dueDateB = b.dueDate?.toString()||""; - - // Check if 'dueDate' property exists in both 'a' and 'b' - if (dueDateA && dueDateB) { - return dueDateA.localeCompare(dueDateB); - } else if (dueDateA) { - // If 'dueDate' exists in 'a' but not in 'b', consider 'a' to come before 'b' - return -1; - } else if (dueDateB) { - // If 'dueDate' exists in 'b' but not in 'a', consider 'b' to come before 'a' - return 1; - } else { - // If 'dueDate' doesn't exist in both 'a' and 'b', maintain their original order - return 0; - } - }); - return RSVP.hash({ docsWaitingForReview: docsWaitingForReview, docsReviewed: docsReviewed, From 73ea4126e1a866d8a00a3a37b13967b6839275e3 Mon Sep 17 00:00:00 2001 From: soujash-mandal <2002soujash@gmail.com> Date: Tue, 25 Jul 2023 17:40:29 +0530 Subject: [PATCH 11/11] Update waiting-for-me.js --- web/app/routes/authenticated/waiting-for-me.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/web/app/routes/authenticated/waiting-for-me.js b/web/app/routes/authenticated/waiting-for-me.js index c8e7a12af..bac3f4d6c 100644 --- a/web/app/routes/authenticated/waiting-for-me.js +++ b/web/app/routes/authenticated/waiting-for-me.js @@ -8,7 +8,6 @@ export default class WaitingForMeRoute extends Route { @service algolia; @service("config") configSvc; @service("fetch") fetchSvc; - @service("recently-viewed-docs") recentDocs; @service session; @service authenticatedUser; @@ -98,17 +97,7 @@ export default class WaitingForMeRoute extends Route { return result.hits; }); - await this.recentDocs.fetchAll.perform(); - if (this.recentDocs.all === null) { - try { - await this.recentDocs.fetchAll.perform(); - } catch { - /** - * This tells our template to show the error state. - */ - this.recentDocs.all = null; - } - } + return RSVP.hash({ docsWaitingForReview: docsWaitingForReview, docsReviewed: docsReviewed,