From 35ef06d27a840149ad15c53f99bca7694386581a Mon Sep 17 00:00:00 2001
From: Ken Tandrian <60643640+KenTandrian@users.noreply.github.com>
Date: Sun, 9 Feb 2025 16:24:44 +0700
Subject: [PATCH] Feature/allow creating custom tags in tag selector component
 (#4305)

* feat(ui): allow creating custom tags

* feat(ui): add new story

* Update changelog
---
 CHANGELOG.md                                       |  1 +
 .../lib/tags-selector/tags-selector.component.html | 11 +++++++++++
 .../tags-selector.component.stories.ts             | 14 ++++++++++++++
 .../lib/tags-selector/tags-selector.component.ts   | 11 ++++++++++-
 4 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4cae16cac..295158c97 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 
 - Extended the tags selector component by a `readonly` attribute
+- Extended the tags selector component to support creating custom tags
 - Added global styles to the _Storybook_ setup
 
 ## 2.138.0 - 2025-02-08
diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.html b/libs/ui/src/lib/tags-selector/tags-selector.component.html
index 1b6817724..4f3929423 100644
--- a/libs/ui/src/lib/tags-selector/tags-selector.component.html
+++ b/libs/ui/src/lib/tags-selector/tags-selector.component.html
@@ -42,6 +42,17 @@
               {{ tag.name }}
             </mat-option>
           }
+
+          @if (hasPermissionToCreateTags && tagInputControl.value) {
+            <mat-option [value]="tagInputControl.value.trim()">
+              <span class="align-items-center d-flex">
+                <ion-icon class="mr-2" name="add-circle-outline" />
+                <ng-container i18n>Create</ng-container> "{{
+                  tagInputControl.value.trim()
+                }}"
+              </span>
+            </mat-option>
+          }
         </mat-autocomplete>
       </mat-form-field>
     }
diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts b/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts
index 4fd0f7e77..8d1431b46 100644
--- a/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts
+++ b/libs/ui/src/lib/tags-selector/tags-selector.component.stories.ts
@@ -47,6 +47,20 @@ export const Default: Story = {
   }
 };
 
+export const CreateCustomTags: Story = {
+  args: {
+    hasPermissionToCreateTags: true,
+    tags: [
+      {
+        id: 'EMERGENCY_FUND',
+        name: 'Emergency Fund',
+        userId: null
+      }
+    ],
+    tagsAvailable: OPTIONS
+  }
+};
+
 export const Readonly: Story = {
   args: {
     readonly: true,
diff --git a/libs/ui/src/lib/tags-selector/tags-selector.component.ts b/libs/ui/src/lib/tags-selector/tags-selector.component.ts
index 6b59d53f4..611c1e938 100644
--- a/libs/ui/src/lib/tags-selector/tags-selector.component.ts
+++ b/libs/ui/src/lib/tags-selector/tags-selector.component.ts
@@ -42,6 +42,7 @@ import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
   templateUrl: 'tags-selector.component.html'
 })
 export class GfTagsSelectorComponent implements OnInit, OnChanges, OnDestroy {
+  @Input() hasPermissionToCreateTags = false;
   @Input() readonly = false;
   @Input() tags: Tag[];
   @Input() tagsAvailable: Tag[];
@@ -76,10 +77,18 @@ export class GfTagsSelectorComponent implements OnInit, OnChanges, OnDestroy {
   }
 
   public onAddTag(event: MatAutocompleteSelectedEvent) {
-    const tag = this.tagsAvailable.find(({ id }) => {
+    let tag = this.tagsAvailable.find(({ id }) => {
       return id === event.option.value;
     });
 
+    if (!tag && this.hasPermissionToCreateTags) {
+      tag = {
+        id: undefined,
+        name: event.option.value as string,
+        userId: null
+      };
+    }
+
     this.tagsSelected.update((tags) => {
       return [...(tags ?? []), tag];
     });