Repository: dhruvdhaduk-simform/fair-share
Pull request #15: Feature/template improvement
Author: login: krushna-simform
Labels: name: enhancement
description: New feature or request
Created: 2025-04-10T12:10:08Z | Merged: 2025-04-14T11:07:30Z

# Description

- Removed usage of `innerHTML` in rendering part.
- Implemented `Edit` Functionality for Update Expense.
- Add limit for Participants in expense.
- Reconfigure view detail section for show history.
- Added delete functionality for expense

# Type of change

- [ ] Bug fix
- [x] New feature
- [ ] Documentation update
- [ ] Refractoring
- [ ] Performance improvement
- [ ] Scaffolding

# Screenshots

![Screenshot from 2025-04-10 17-35-58](https://github.com/user-attachments/assets/a4015e0c-d96c-4451-8c55-3f06d9a33a2c)
![Screenshot from 2025-04-10 17-35-06](https://github.com/user-attachments/assets/46477e2f-0198-4fd3-a53a-aa42bc822204)
![Screenshot from 2025-04-10 17-35-27](https://github.com/user-attachments/assets/d9333721-06bc-46db-b1c0-36a502c17da5)
![Screenshot from 2025-04-11 15-14-55](https://github.com/user-attachments/assets/ad479f41-bcb9-4ec4-9342-f96fa868ccc3)


# Validations

![Screenshot from 2025-04-10 17-44-29](https://github.com/user-attachments/assets/765ff255-9be5-45fb-a892-11b7d0656f8c)
![Screenshot from 2025-04-10 17-44-40](https://github.com/user-attachments/assets/8bb69ce6-5ad9-49de-9b86-b9507294c4f2)
![Screenshot from 2025-04-10 17-44-48](https://github.com/user-attachments/assets/1dda8113-8b60-4b33-bf98-6aebeca52238)


# How to test

Deployment Preview: https://fair-share-git-fork-krus-3be816-dhruv-dhaduks-projects-6e66c815.vercel.app




Diff:
diff --git a/src/assets/icons/delete.svg b/src/assets/icons/delete.svg
new file mode 100644
index 0000000..64c23a5
--- /dev/null
+++ b/src/assets/icons/delete.svg
@@ -0,0 +1,7 @@
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="SVGRepo_bgCarrier" stroke-width="0"/>
+<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
+<g id="SVGRepo_iconCarrier"> <path d="M10 11V17" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M14 11V17" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M4 7H20" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M6 7H12H18V18C18 19.6569 16.6569 21 15 21H9C7.34315 21 6 19.6569 6 18V7Z" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g>
+</svg>
\ No newline at end of file
diff --git a/src/scripts/controller/ExpenseController.ts b/src/scripts/controller/ExpenseController.ts
index a31dda0..ed075fb 100644
--- a/src/scripts/controller/ExpenseController.ts
+++ b/src/scripts/controller/ExpenseController.ts
@@ -10,6 +10,7 @@ export class ExpenseController {
     #expenses: Array<Expense>;
     #addExpenseForm: HTMLFormElement;
     #view: ExpensesView;
+    #editExpense: Expense | null = null;
 
     constructor() {
         // Retrive stored participants and expenses from localStorage.
@@ -28,6 +29,19 @@ export class ExpenseController {
             this.handleAddExpenseFormSubmit();
         });
 
+        const addButton = document.querySelector(
+            SELECTORS.addExpensePopupButton
+        ) as HTMLButtonElement;
+
+        addButton.addEventListener('click', () => {
+            if (this.#editExpense) {
+                this.#editExpense = null;
+                this.#addExpenseForm.reset();
+                this.clearParticipants();
+                this.setFormMode(false);
+            }
+        });
+
         // Render expenses fetched from localStorage.
         this.#view.renderExpenses(
             this.#expenses,
@@ -39,6 +53,7 @@ export class ExpenseController {
         this.handleAddParticipant();
         this.attachSettlePaymentHandlers();
         this.attachValidationHandlers();
+        this.deleteExpenseHandler();
     }
 
     // Return a participant by name. Or create new one if doesn't exist.
@@ -117,6 +132,78 @@ export class ExpenseController {
             this.#expenses,
             this.attachSettlePaymentHandlers.bind(this)
         );
+
+        this.deleteExpenseHandler();
+    }
+
+    updateExpense(
+        expenseId: string,
+        title: string,
+        description: string,
+        amount: number,
+        participantNames: Array<string>,
+        paidByPersonName: string
+    ) {
+        expenseId = expenseId.trim();
+        const expense = this.#expenses.find((exp) => exp.id === expenseId);
+        if (!expense) {
+            throw new Error('Expense with specified ID not found.');
+        }
+
+        title = title.trim();
+        description = description.trim();
+        participantNames = participantNames.map((p) => p.trim());
+        paidByPersonName = paidByPersonName.trim();
+
+        // Get participants by their names.
+        const participants: Array<Participant> = participantNames.map((p) => {
+            return this.getParticipant(p);
+        });
+
+        const paidBy = this.getParticipant(paidByPersonName);
+        const notSettled = participants.filter((p) => p.name !== paidBy.name);
+        const calculatedAmount = amount - amount / participants.length;
+
+        // Update the balance of participants according to this expense.
+        paidBy.balance += calculatedAmount;
+        notSettled.forEach((p) => {
+            p.balance -= calculatedAmount / notSettled.length;
+        });
+
+        expense.title = title;
+        expense.description = description;
+        expense.originalAmount = amount;
+        expense.calculatedAmount = calculatedAmount;
+        expense.paidBy = paidBy;
+        expense.settled = [];
+        expense.notSettled = notSettled;
+
+        // Update localStorage to include new expense and participants.
+        StorageService.saveParticipants(this.#participants);
+        StorageService.saveExpenses(this.#expenses);
+        // Render expenses with new one added.
+        this.#view.renderExpenses(
+            this.#expenses,
+            this.attachSettlePaymentHandlers.bind(this)
+        );
+
+        this.deleteExpenseHandler();
+    }
+
+    deleteExpense(expenseId: string) {
+        expenseId = expenseId.trim();
+
+        console.log(expenseId);
+
+        this.#expenses = this.#expenses.filter((exp) => exp.id !== expenseId);
+        StorageService.saveExpenses(this.#expenses);
+
+        this.#view.renderExpenses(
+            this.#expenses,
+            this.attachSettlePaymentHandlers.bind(this)
+        );
+
+        this.deleteExpenseHandler();
     }
 
     // Settle an expense for a particular participant.
@@ -157,6 +244,8 @@ export class ExpenseController {
             this.attachSettlePaymentHandlers.bind(this)
         );
 
+        this.deleteExpenseHandler();
+
         // Update localStorage to include new expense and participants.
         StorageService.saveExpenses(this.#expenses);
         StorageService.saveParticipants(this.#participants);
@@ -203,13 +292,24 @@ export class ExpenseController {
             .filter((participant) => typeof participant === 'string');
 
         try {
-            this.addNewExpense(
-                title,
-                description,
-                amount,
-                participantNames,
-                paidByPersonName
-            );
+            if (this.#editExpense == null) {
+                this.addNewExpense(
+                    title,
+                    description,
+                    amount,
+                    participantNames,
+                    paidByPersonName
+                );
+            } else {
+                this.updateExpense(
+                    this.#editExpense.id,
+                    title,
+                    description,
+                    amount,
+                    participantNames,
+                    paidByPersonName
+                );
+            }
         } catch (err: unknown) {
             if (err instanceof Error) {
                 alert(err.message);
@@ -230,6 +330,14 @@ export class ExpenseController {
         if (popup instanceof HTMLDivElement) {
             popup.hidePopover();
         }
+        if (this.#editExpense) {
+            this.#view.renderExpenseDetails(
+                this.#editExpense,
+                this.attachSettlePaymentHandlers.bind(this)
+            );
+        }
+
+        FormService.clearError('participant');
     }
 
     // Attach event handler to theme toggle button.
@@ -252,53 +360,66 @@ export class ExpenseController {
         });
     }
 
-    // Attach event handler to Add Participant button in Add Expense form.
-    handleAddParticipant() {
-        const addParticipantBtn = this.#addExpenseForm.querySelector(
-            SELECTORS.addParticipantButton
-        ) as HTMLButtonElement;
-
+    addParticipant(participantName: string) {
         const participantInput = this.#addExpenseForm.querySelector(
             SELECTORS.participantInput
         ) as HTMLInputElement;
 
-        // Utility function which will actually add a participant.
-        const addParticipant = () => {
-            const participantName = participantInput.value;
+        const participantContainer = this.#addExpenseForm.querySelector(
+            SELECTORS.participants
+        ) as HTMLElement;
+
+        if (participantContainer.childElementCount > 7) {
+            return;
+        }
 
-            const participantError =
-                FormService.validateParticipant(participantName);
+        const participantError =
+            FormService.validateParticipant(participantName);
 
-            if (participantError) return;
+        if (participantError) return;
 
-            this.#view.renderParticipant(participantName);
+        FormService.clearError('participant');
 
-            participantInput.value = '';
+        this.#view.renderParticipant(participantName);
 
-            // Attach event handlers to Remove button for each participant.
-            Array.from(
-                this.#addExpenseForm.querySelector(SELECTORS.participants)
-                    ?.children ?? []
-            ).forEach((item) => {
-                // For each participant in participants list.
+        participantInput.value = '';
 
-                const name = item.textContent?.trim() ?? '';
-                item.querySelector('img')?.addEventListener('click', () => {
-                    item.remove();
-                    this.#addExpenseForm
-                        .querySelector(SELECTORS.paidByOption(name))
-                        ?.remove();
-                });
+        // Attach event handlers to Remove button for each participant.
+        Array.from(
+            this.#addExpenseForm.querySelector(SELECTORS.participants)
+                ?.children ?? []
+        ).forEach((item) => {
+            // For each participant in participants list.
+
+            const name = item.textContent?.trim() ?? '';
+            item.querySelector('img')?.addEventListener('click', () => {
+                item.remove();
+                this.#addExpenseForm
+                    .querySelector(SELECTORS.paidByOption(name))
+                    ?.remove();
             });
+        });
 
-            participantInput.focus();
-        };
+        participantInput.focus();
+    }
+
+    // Attach event handler to Add Participant button in Add Expense form.
+    handleAddParticipant() {
+        const addParticipantBtn = this.#addExpenseForm.querySelector(
+            SELECTORS.addParticipantButton
+        ) as HTMLButtonElement;
+
+        const participantInput = this.#addExpenseForm.querySelector(
+            SELECTORS.participantInput
+        ) as HTMLInputElement;
 
-        addParticipantBtn.addEventListener('click', addParticipant);
+        addParticipantBtn.addEventListener('click', () => {
+            this.addParticipant(participantInput.value);
+        });
         participantInput.addEventListener('keydown', (e) => {
             if (e.key === 'Enter') {
                 e.preventDefault();
-                addParticipant();
+                this.addParticipant(participantInput.value);
             }
         });
     }
@@ -318,12 +439,32 @@ export class ExpenseController {
         participantOptions.innerHTML = `<option value="" disabled selected> select participant </option>`;
     }
 
+    deleteExpenseHandler() {
+        const deleteButton = document.querySelectorAll(
+            '.expense-delete-button'
+        ) as NodeListOf<HTMLButtonElement>;
+
+        deleteButton.forEach((button) => {
+            button.addEventListener('click', () => {
+                const expenseId = button.dataset.expenseId;
+                if (typeof expenseId === 'string') {
+                    console.log('first');
+                    this.deleteExpense(expenseId);
+                }
+            });
+        });
+    }
+
     // Attach event handler to 'Paid' buttons for settling payments.
     attachSettlePaymentHandlers() {
         const expenceDetailContainer = document.querySelector(
             SELECTORS.expenseDetailContainer
         ) as HTMLElement;
 
+        const editButton = expenceDetailContainer.querySelector(
+            SELECTORS.expenseEditButton
+        ) as HTMLButtonElement;
+
         expenceDetailContainer
             .querySelectorAll(SELECTORS.expensePaidButtons)
             .forEach((paidBtn) => {
@@ -335,6 +476,39 @@ export class ExpenseController {
                     this.settle(expenseId, participantId);
                 });
             });
+
+        editButton?.addEventListener('click', () => {
+            const setValue = (selector: string, value: string | number) => {
+                (
+                    this.#addExpenseForm.querySelector(
+                        selector
+                    ) as HTMLInputElement
+                ).value = String(value);
+            };
+
+            const expenseId = editButton.dataset.expenseId?.trim();
+            const expense = this.#expenses.find((exp) => exp.id === expenseId);
+            if (!expense) {
+                throw new Error('Expense with specified ID not found.');
+            }
+
+            this.clearParticipants();
+
+            setValue(SELECTORS.titleInput, expense.title);
+            setValue(SELECTORS.descriptionInput, expense.description);
+            setValue(SELECTORS.amountInput, expense.originalAmount);
+            setValue(SELECTORS.paidByInput, expense.paidBy.name);
+
+            this.addParticipant(expense.paidBy.name);
+
+            expense.notSettled.forEach((participant) => {
+                this.addParticipant(participant.name);
+            });
+
+            this.#editExpense = expense;
+
+            this.setFormMode(true);
+        });
     }
 
     // Attach inline validation handlers to all form inputs.
@@ -343,7 +517,8 @@ export class ExpenseController {
         const handleInput = (
             inputSelector: string,
             validator: (value: string) => string,
-            errorType: string
+            errorType: string,
+            limitValidator?: () => string
         ) => {
             const inputElement = this.#addExpenseForm.querySelector(
                 inputSelector
@@ -351,7 +526,15 @@ export class ExpenseController {
             inputElement.addEventListener('input', (e) => {
                 const value =
                     e.target instanceof HTMLInputElement ? e.target.value : '';
-                const errorMessage = validator(value);
+                let errorMessage: string;
+                errorMessage = validator(value);
+                if (limitValidator) {
+                    const limitError = limitValidator();
+                    if (limitError) {
+                        errorMessage = limitError;
+                        (e.target as HTMLInputElement).value = '';
+                    }
+                }
                 if (errorMessage) {
                     FormService.showError(errorType, errorMessage);
                 } else {
@@ -361,11 +544,54 @@ export class ExpenseController {
         };
 
         formInputList.forEach((inputItem) => {
-            handleInput(
-                inputItem.inputSelector,
-                inputItem.validator,
-                inputItem.errorType
-            );
+            if (inputItem.errorType == 'participant') {
+                handleInput(
+                    inputItem.inputSelector,
+                    inputItem.validator,
+                    inputItem.errorType,
+                    () => {
+                        const participantContainer =
+                            this.#addExpenseForm.querySelector(
+                                SELECTORS.participants
+                            ) as HTMLElement;
+
+                        if (participantContainer.childElementCount > 7) {
+                            return 'You can add only 8 participants.';
+                        }
+                        return '';
+                    }
+                );
+            } else {
+                handleInput(
+                    inputItem.inputSelector,
+                    inputItem.validator,
+                    inputItem.errorType
+                );
+            }
         });
     }
+
+    setFormMode(isEditMode: boolean) {
+        if (isEditMode) {
+            (
+                document.querySelector(SELECTORS.formHeading) as HTMLElement
+            ).textContent = 'Update Expense';
+
+            (
+                document.querySelector(
+                    SELECTORS.formSubmitButton
+                ) as HTMLButtonElement
+            ).textContent = 'Update Expense';
+        } else {
+            (
+                document.querySelector(SELECTORS.formHeading) as HTMLElement
+            ).textContent = 'Add New Expense';
+
+            (
+                document.querySelector(
+                    SELECTORS.formSubmitButton
+                ) as HTMLButtonElement
+            ).textContent = 'Add Expense';
+        }
+    }
 }
diff --git a/src/scripts/index.ts b/src/scripts/index.ts
index d0bc328..d1353ab 100644
--- a/src/scripts/index.ts
+++ b/src/scripts/index.ts
@@ -1,16 +1,2 @@
-/**
- * [TEMPORARY]
- * This is for temporary snippet for testing purpose only.
- * It allows reviewers to test the Controller logic from console, because UI has not benn implemented yet.
- */
 import { ExpenseController } from './controller/ExpenseController.ts';
-declare global {
-    interface Window {
-        e: ExpenseController;
-    }
-}
-window.e = new ExpenseController();
-
-const greet: string = 'Hello World from TypeScript';
-
-console.log(greet);
+new ExpenseController();
diff --git a/src/scripts/utils/selectors.ts b/src/scripts/utils/selectors.ts
index 5fc6796..f403f25 100644
--- a/src/scripts/utils/selectors.ts
+++ b/src/scripts/utils/selectors.ts
@@ -13,4 +13,9 @@ export const SELECTORS = Object.freeze({
     expensePaidButtons: '.expense-paid-btn',
     recentExpensesContainer: '.recent-expenses',
     viewDetailsButtons: '.view-details-button',
+    expenseEditButton: '.edit-button',
+    addExpensePopupButton: '#expense-btn',
+    formHeading: '.form-heading',
+    formSubmitButton: '.add-expense-btn',
+    deleteExpenseButton: '.view-details-button',
 });
diff --git a/src/scripts/view/ExpensesView.ts b/src/scripts/view/ExpensesView.ts
index ee20961..22e5300 100644
--- a/src/scripts/view/ExpensesView.ts
+++ b/src/scripts/view/ExpensesView.ts
@@ -33,9 +33,18 @@ export class ExpensesView {
     ): void {
         if (!this.#recentExpensesContainer) return;
 
-        this.#recentExpensesContainer.innerHTML = expenses
-            .map(templates.recentExpenseList)
-            .join('');
+        this.#recentExpensesContainer.textContent = '';
+
+        if (!(expenses.length == 0)) {
+            expenses.forEach((expense) => {
+                const expenseElement = templates.recentExpenseList(expense);
+                this.#recentExpensesContainer.prepend(expenseElement);
+            });
+        } else {
+            this.#recentExpensesContainer.prepend(
+                templates.initialRecentExpense()
+            );
+        }
 
         this.handleViewDetailBtn(expenses, attachEventHandlers);
     }
@@ -43,10 +52,11 @@ export class ExpensesView {
     renderParticipant(name: string): void {
         if (!this.#participants || !this.#participantPaidByOptions) return;
 
-        this.#participants.innerHTML += templates.formParticipants(name);
+        const participantElement = templates.formParticipants(name);
+        this.#participants.appendChild(participantElement);
 
-        this.#participantPaidByOptions.innerHTML +=
-            templates.formPaidByOption(name);
+        const paidByOptionElement = templates.formPaidByOption(name);
+        this.#participantPaidByOptions.appendChild(paidByOptionElement);
     }
 
     handleViewDetailBtn(
@@ -75,8 +85,12 @@ export class ExpensesView {
     }
 
     renderExpenseDetails(expense: Expense, attachEventHandlers: () => void) {
-        this.#expenceDetailContainer.innerHTML =
-            templates.expenseDetails(expense);
+        if (!this.#expenceDetailContainer) return;
+
+        this.#expenceDetailContainer.textContent = '';
+
+        const expenseDetailsElement = templates.expenseDetails(expense);
+        this.#expenceDetailContainer.appendChild(expenseDetailsElement);
 
         attachEventHandlers();
     }
diff --git a/src/scripts/view/template.ts b/src/scripts/view/template.ts
index a07913c..f9fa354 100644
--- a/src/scripts/view/template.ts
+++ b/src/scripts/view/template.ts
@@ -1,68 +1,193 @@
-import type { Expense } from '../model/interfaces';
+import { Expense } from '../model/interfaces';
 
 export const templates = {
-    recentExpenseList: (expense: Expense) => {
-        return `
-            <li>
-                <p>${expense.title}</p>
-                <p>$${expense.calculatedAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')})</p>
-                <button class="view-details-button" popovertarget="expense-detail" data-expense-id="${expense.id}">View Details</button>
-            </li>
-        `;
+    /* This function serves as a template for displaying the list of recent expenses */
+    recentExpenseList: (expense: Expense): HTMLElement => {
+        const listItem = document.createElement('li');
+
+        const title = document.createElement('p');
+        title.textContent = expense.title;
+
+        listItem.appendChild(title);
+
+        if (expense.calculatedAmount === 0) {
+            const message = document.createElement('p');
+            message.classList.add('payment-settle-message');
+            message.textContent = 'This expense has been successfully resolved';
+            listItem.appendChild(message);
+        } else {
+            const amount = document.createElement('p');
+            amount.textContent = templates.expenseSplitDisplay(expense);
+            listItem.appendChild(amount);
+        }
+
+        const buttonController = document.createElement('div');
+        buttonController.classList.add('button-controller');
+        listItem.appendChild(buttonController);
+
+        const viewDetailsButton = document.createElement('button');
+        viewDetailsButton.classList.add('view-details-button');
+        viewDetailsButton.setAttribute('data-expense-id', expense.id);
+        viewDetailsButton.setAttribute('popovertarget', 'expense-detail');
+        viewDetailsButton.textContent = 'View Details';
+        buttonController.appendChild(viewDetailsButton);
+
+        const deleteButtonIcon = document.createElement('img');
+        deleteButtonIcon.src = `${new URL('../../assets/icons/delete.svg', import.meta.url)}`;
+        deleteButtonIcon.alt = 'delete icon';
+        deleteButtonIcon.classList.add('delete-button-icon');
+
+        const deleteButton = document.createElement('button');
+        deleteButton.classList.add('expense-delete-button');
+        deleteButton.setAttribute('data-expense-id', expense.id);
+        deleteButton.appendChild(deleteButtonIcon);
+
+        buttonController.appendChild(deleteButton);
+
+        return listItem;
     },
 
-    expenseDetails: (expense: Expense) => {
-        return `
-            <p class="expense-detail-title">${expense.title}</p>
-            <p class="expense-detail-description">${expense.description}</p>
-            <p class="expense-detail-split">$${expense.calculatedAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')})</p>
-            ${templates.expenseParticipants(expense)}
-        `;
+    initialRecentExpense: (): HTMLElement => {
+        const message = document.createElement('p');
+        message.textContent =
+            'No recent expenses found, please add one to get started.';
+
+        return message;
+    },
+
+    /* This function serves as a template that provides information about expenses paid by someone and how they are split with participants */
+    expenseSplitDisplay: (expense: Expense) => {
+        const participants = [...expense.settled, ...expense.notSettled];
+        return `\u20B9${expense.originalAmount} (${expense.paidBy.name} paid, split with ${participants.map((participant) => participant.name).join(', ')})`;
+    },
+
+    /* This function provides expense details for a particular expense, including participants' names and their payment status */
+    expenseDetails: (expense: Expense): HTMLElement => {
+        const container = document.createElement('div');
+
+        const title = document.createElement('p');
+        title.classList.add('expense-detail-title');
+        title.textContent = expense.title;
+
+        const closeIcon = document.createElement('img');
+        closeIcon.src = `${new URL('../../assets/icons/close.svg', import.meta.url)}`;
+        closeIcon.alt = 'close icon';
+
+        const closeButton = document.createElement('button');
+        closeButton.setAttribute('popovertarget', 'expense-detail');
+        closeButton.classList.add('view-detail-close-button');
+        closeButton.appendChild(closeIcon);
+
+        const titleDiv = document.createElement('div');
+        titleDiv.classList.add('view-detail-title-div');
+
+        titleDiv.appendChild(title);
+        titleDiv.appendChild(closeButton);
+        container.appendChild(titleDiv);
+
+        const description = document.createElement('p');
+        description.classList.add('expense-detail-description');
+        description.textContent = expense.description;
+        container.appendChild(description);
+
+        const splitAmount = document.createElement('p');
+        splitAmount.classList.add('expense-detail-split');
+        splitAmount.textContent = templates.expenseSplitDisplay(expense);
+        container.appendChild(splitAmount);
+
+        const participants = templates.expenseParticipants(expense);
+        container.appendChild(participants);
+
+        const editButton = document.createElement('button');
+        editButton.textContent = 'Edit';
+        editButton.classList.add('edit-button');
+        editButton.dataset.expenseId = expense.id;
+        editButton.setAttribute('popovertarget', 'form-container');
+
+        if (expense.settled.length == 0) {
+            container.appendChild(editButton);
+        }
+
+        return container;
     },
 
-    expenseParticipants: (expense: Expense) => {
-        return `
-            <ul class="expense-detail-participants">
-                <li>
-                    <P>
-                        ${expense.paidBy.name}
-                        <span class="to-receive">to receive $${expense.calculatedAmount}</span>
-                    </P>
-                </li>
-                ${expense.notSettled
-                    .map(
-                        (participant) => `
-                        <li>
-                            <p>
-                                ${participant.name}
-                                <span class="to-pay">to pay $${expense.calculatedAmount / expense.notSettled.length}</span>
-                            </p>
-                            <button data-expense-id="${expense.id}" data-participant-id="${participant.id}" class="expense-paid-btn">Paid</button>
-                        </li>
-                    `
-                    )
-                    .join('')}
-            </ul>
-
-        `;
+    /* This function provides participants with their expense details, including their payment status */
+    expenseParticipants: (expense: Expense): HTMLElement => {
+        const participantsList = document.createElement('ul');
+        participantsList.classList.add('expense-detail-participants');
+
+        const paidByItem = document.createElement('li');
+        const paidByText = document.createElement('p');
+        const spanElement = document.createElement('span');
+        spanElement.className = 'to-receive';
+        spanElement.textContent = `to receive ₹${expense.calculatedAmount.toFixed(2)}`;
+        paidByText.textContent = `${expense.paidBy.name} `;
+        paidByText.appendChild(spanElement);
+        paidByItem.appendChild(paidByText);
+        participantsList.appendChild(paidByItem);
+
+        expense.notSettled.forEach((participant) => {
+            const participantItem = document.createElement('li');
+            const participantText = document.createElement('p');
+            const spanElement = document.createElement('span');
+            spanElement.className = 'to-pay';
+            spanElement.textContent = `to pay ₹${(expense.calculatedAmount / expense.notSettled.length).toFixed(2)}`;
+            participantText.textContent = `${participant.name} `;
+            participantText.appendChild(spanElement);
+            participantItem.appendChild(participantText);
+
+            const paidButton = document.createElement('button');
+            paidButton.classList.add('expense-paid-btn');
+            paidButton.setAttribute('data-expense-id', expense.id);
+            paidButton.setAttribute('data-participant-id', participant.id);
+            paidButton.textContent = 'Paid';
+            participantItem.appendChild(paidButton);
+
+            participantsList.appendChild(participantItem);
+        });
+
+        expense.settled.forEach((participant) => {
+            const participantItem = document.createElement('li');
+            const participantText = document.createElement('p');
+            participantText.classList.add('paid-list');
+
+            const spanElement = document.createElement('span');
+            spanElement.className = 'paid';
+            spanElement.textContent = `paid ₹${(expense.originalAmount / (expense.notSettled.length + expense.settled.length + 1)).toFixed(2)}`;
+
+            participantText.textContent = `${participant.name} `;
+            participantText.appendChild(spanElement);
+
+            participantItem.appendChild(participantText);
+            participantsList.appendChild(participantItem);
+        });
+
+        return participantsList;
     },
 
-    formParticipants: (name: string) => {
-        return `
-            <span>${name}
-                <img
-                    src="${new URL('../../assets/icons/close.svg', import.meta.url)}"
-                    alt="remove participant"
-                    class="remove-participant"
-                    role="button"
-                />
-            </span>
-        `;
+    /* This function is used to render the participant's name in the form when a participant is added */
+    formParticipants: (name: string): HTMLElement => {
+        const participantSpan = document.createElement('span');
+        participantSpan.textContent = name;
+
+        const closeIcon = document.createElement('img');
+        closeIcon.setAttribute(
+            'src',
+            new URL('../../assets/icons/close.svg', import.meta.url).toString()
+        );
+        closeIcon.setAttribute('alt', 'remove participant');
+        closeIcon.classList.add('remove-participant');
+        closeIcon.setAttribute('role', 'button');
+        participantSpan.appendChild(closeIcon);
+
+        return participantSpan;
     },
 
-    formPaidByOption: (name: string) => {
-        return `
-            <option value="${name}">${name}</option>
-        `;
+    /* This function renders participants' names inside a select option */
+    formPaidByOption: (name: string): HTMLOptionElement => {
+        const option = document.createElement('option');
+        option.setAttribute('value', name);
+        option.textContent = name;
+        return option;
     },
 };
diff --git a/src/styles/form.css b/src/styles/form.css
index 19e8b10..80b88d3 100644
--- a/src/styles/form.css
+++ b/src/styles/form.css
@@ -79,7 +79,9 @@ select {
 
     display: inline-flex;
     align-items: center;
-    gap: 0.3rem;
+    gap: 0.5rem;
+
+    margin: 0 0.2rem;
 }
 
 .participants-input-container {
diff --git a/src/styles/main.css b/src/styles/main.css
index 6413310..9b16f19 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -7,8 +7,7 @@
 }
 
 /* main container title */
-.main-container-title,
-.expense-detail-title {
+.main-container-title {
     font-size: 1.5rem;
     color: var(--primary-color);
     font-weight: 600;
@@ -25,6 +24,18 @@
     border-bottom: 1px solid var(--border-color);
 }
 
+.recent-expenses li > p:first-child {
+    width: 10rem;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    overflow: hidden;
+}
+
+.button-controller {
+    display: flex;
+    align-items: center;
+}
+
 .view-details-button {
     background-color: var(--primary-color);
     color: white;
@@ -40,6 +51,25 @@
     cursor: pointer;
 }
 
+.expense-delete-button {
+    background-color: var(--danger-color);
+    color: white;
+    border: none;
+    padding: 0.2rem 0.6em;
+    border-radius: 0.25rem;
+    font-weight: 600;
+    margin-left: 0.4rem;
+}
+
+.expense-delete-button:hover {
+    opacity: 0.7;
+    cursor: pointer;
+}
+
+.delete-button-icon {
+    width: 1.6rem;
+}
+
 /* expense details popup */
 .expense-detail {
     background: var(--card-bg);
@@ -53,8 +83,18 @@
 }
 
 .expense-detail-title {
+    font-size: 1.5rem;
+    color: var(--primary-color);
+    font-weight: 600;
+}
+
+.view-detail-title-div {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
     border-bottom: 1px solid var(--border-color);
-    padding-bottom: 0.8rem;
+    margin-bottom: 1rem;
+    padding: 0.5rem 0;
 }
 
 .expense-detail-participants {
@@ -104,6 +144,53 @@
     cursor: pointer;
 }
 
+.paid {
+    color: var(--success-color);
+    font-weight: 600;
+    margin-left: 0.6rem;
+}
+
+.payment-settle-message {
+    color: var(--success-color);
+    font-weight: 600;
+}
+
+.edit-button {
+    background-color: var(--primary-color);
+    color: white;
+    color: white;
+    border: none;
+    padding: 0.5rem 1rem;
+    border-radius: 0.25rem;
+    font-weight: 600;
+    transition: background-color 0.3s;
+}
+
+.edit-button:hover {
+    background-color: var(--secondary-color);
+    cursor: pointer;
+}
+
+.view-detail-close-button {
+    background-color: var(--danger-color);
+    border: none;
+    padding: 0.3rem;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.view-detail-close-button:hover {
+    cursor: pointer;
+    opacity: 0.8;
+}
+
+.view-detail-close-button img {
+    filter: invert(100%);
+    height: 0.8rem;
+}
+
 body:not(#expense-detail:has(:popover-open)) main {
     transition-duration: 300ms;
 }


Reviews:
user: login: dhruv-d-simform
state: APPROVED
author_association: OWNER
submitted_at: 2025-04-10T12:25:16Z
user: login: Sakshi-Simform
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-10T12:28:23Z
user: login: krushna-simform
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-11T08:12:15Z
user: login: Kaushal-SF
body: Wonderful Job! 👏

Added few minor suggestions. Otherwise code looks good.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-13T16:56:26Z
user: login: krushna-simform
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T09:02:08Z
user: login: krushna-simform
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T09:02:51Z
user: login: krushna-simform
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T09:07:20Z
user: login: Kaushal-SF
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T09:41:50Z
user: login: Kaushal-SF
body: Changes look good. Added minor comments.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T09:49:55Z
user: login: krushna-simform
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T10:39:30Z
user: login: Kaushal-SF
body: Changes look good. Added few minor observations. 

Once you make the changes feel to merge the PR. No further review required.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-14T10:57:36Z

Review comments:
diff_hunk: @@ -343,15 +478,27 @@ export class ExpenseController {
         const handleInput = (
             inputSelector: string,
             validator: (value: string) => string,
-            errorType: string
+            errorType: string,
+            limitValidator?: () => string
         ) => {
             const inputElement = this.#addExpenseForm.querySelector(
                 inputSelector
             ) as HTMLInputElement;
             inputElement.addEventListener('input', (e) => {
+                console.log('CHECK');
                 const value =
                     e.target instanceof HTMLInputElement ? e.target.value : '';
-                const errorMessage = validator(value);
+                let errorMessage: string;
+                errorMessage = validator(value);
+                if (limitValidator) {
+                    const limitError = limitValidator();
+                    console.log(`limit error: ${limitError}`);
+                    if (limitError) {
+                        errorMessage = limitError;
+                        (e.target as HTMLInputElement).value = '';
+                    }
+                }
+                console.log(`error msg: ${errorMessage}`);
path: src/scripts/controller/ExpenseController.ts
user: login: dhruv-d-simform
body: We can remove these console logs here.
created_at: 2025-04-10T12:23:56Z
author_association: OWNER
diff_hunk: @@ -343,15 +478,27 @@ export class ExpenseController {
         const handleInput = (
             inputSelector: string,
             validator: (value: string) => string,
-            errorType: string
+            errorType: string,
+            limitValidator?: () => string
         ) => {
             const inputElement = this.#addExpenseForm.querySelector(
                 inputSelector
             ) as HTMLInputElement;
             inputElement.addEventListener('input', (e) => {
+                console.log('CHECK');
                 const value =
                     e.target instanceof HTMLInputElement ? e.target.value : '';
-                const errorMessage = validator(value);
+                let errorMessage: string;
+                errorMessage = validator(value);
+                if (limitValidator) {
+                    const limitError = limitValidator();
+                    console.log(`limit error: ${limitError}`);
+                    if (limitError) {
+                        errorMessage = limitError;
+                        (e.target as HTMLInputElement).value = '';
+                    }
+                }
+                console.log(`error msg: ${errorMessage}`);
path: src/scripts/controller/ExpenseController.ts
user: login: krushna-simform
body: Removed extra log statements (https://github.com/dhruvdhaduk-simform/fair-share/pull/15/commits/15bc51ed792d6b43085c2f679bf41485d8a9bfe0)
created_at: 2025-04-11T08:12:15Z
author_association: COLLABORATOR
diff_hunk: @@ -1,68 +1,163 @@
-import type { Expense } from '../model/interfaces';
+import { Expense } from '../model/interfaces';
 
 export const templates = {
-    recentExpenseList: (expense: Expense) => {
-        return `
-            <li>
-                <p>${expense.title}</p>
-                <p>$${expense.calculatedAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')})</p>
-                <button class="view-details-button" popovertarget="expense-detail" data-expense-id="${expense.id}">View Details</button>
-            </li>
-        `;
+    recentExpenseList: (expense: Expense): HTMLElement => {
+        const listItem = document.createElement('li');
+
+        const title = document.createElement('p');
+        title.textContent = expense.title;
+
+        listItem.appendChild(title);
+
+        if (expense.calculatedAmount === 0) {
+            const message = document.createElement('p');
+            message.classList.add('payment-settle-message');
+            message.textContent = 'This expense has been successfully resolved';
+            listItem.appendChild(message);
+        } else {
+            const amount = document.createElement('p');
+            amount.innerHTML = templates.expenseSplitDisplay(expense);
+            listItem.appendChild(amount);
+        }
+
+        const buttonController = document.createElement('div');
+        buttonController.classList.add('button-controller');
+        listItem.appendChild(buttonController);
+
+        const viewDetailsButton = document.createElement('button');
+        viewDetailsButton.classList.add('view-details-button');
+        viewDetailsButton.setAttribute('data-expense-id', expense.id);
+        viewDetailsButton.setAttribute('popovertarget', 'expense-detail');
+        viewDetailsButton.textContent = 'View Details';
+        buttonController.appendChild(viewDetailsButton);
+
+        const deleteButtonIcon = document.createElement('img');
+        deleteButtonIcon.src = `${new URL('../../assets/icons/delete.svg', import.meta.url)}`;
+        deleteButtonIcon.alt = 'delete icon';
+        deleteButtonIcon.classList.add('delete-button-icon');
+
+        const deleteButton = document.createElement('button');
+        deleteButton.classList.add('expense-delete-button');
+        deleteButton.setAttribute('data-expense-id', expense.id);
+        deleteButton.appendChild(deleteButtonIcon);
+
+        buttonController.appendChild(deleteButton);
+
+        return listItem;
+    },
+
+    expenseSplitDisplay: (expense: Expense) => {
+        return `&#x20b9;${expense.originalAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')}, ${expense.settled.map((participant) => participant.name).join(', ')})`;
     },
 
-    expenseDetails: (expense: Expense) => {
-        return `
-            <p class="expense-detail-title">${expense.title}</p>
-            <p class="expense-detail-description">${expense.description}</p>
-            <p class="expense-detail-split">$${expense.calculatedAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')})</p>
-            ${templates.expenseParticipants(expense)}
-        `;
+    expenseDetails: (expense: Expense): HTMLElement => {
+        const container = document.createElement('div');
+
+        const title = document.createElement('p');
+        title.classList.add('expense-detail-title');
+        title.textContent = expense.title;
+
+        const closeIcon = document.createElement('img');
+        closeIcon.src = `${new URL('../../assets/icons/close.svg', import.meta.url)}`;
+        closeIcon.alt = 'close icon';
+
+        const closeButton = document.createElement('button');
+        closeButton.setAttribute('popovertarget', 'expense-detail');
+        closeButton.classList.add('view-detail-close-button');
+        closeButton.appendChild(closeIcon);
+
+        const titleDiv = document.createElement('div');
+        titleDiv.classList.add('view-detail-title-div');
+
+        titleDiv.appendChild(title);
+        titleDiv.appendChild(closeButton);
+        container.appendChild(titleDiv);
+
+        const description = document.createElement('p');
+        description.classList.add('expense-detail-description');
+        description.textContent = expense.description;
+        container.appendChild(description);
+
+        const splitAmount = document.createElement('p');
+        splitAmount.classList.add('expense-detail-split');
+        splitAmount.innerHTML = templates.expenseSplitDisplay(expense);
+        container.appendChild(splitAmount);
+
+        const participants = templates.expenseParticipants(expense);
+        container.appendChild(participants);
+
+        const editButton = document.createElement('button');
+        editButton.textContent = 'Edit';
+        editButton.classList.add('edit-button');
+        editButton.dataset.expenseId = expense.id;
+        editButton.setAttribute('popovertarget', 'form-container');
+
+        if (expense.settled.length == 0) {
+            container.appendChild(editButton);
+        }
+
+        return container;
     },
 
-    expenseParticipants: (expense: Expense) => {
-        return `
-            <ul class="expense-detail-participants">
-                <li>
-                    <P>
-                        ${expense.paidBy.name}
-                        <span class="to-receive">to receive $${expense.calculatedAmount}</span>
-                    </P>
-                </li>
-                ${expense.notSettled
-                    .map(
-                        (participant) => `
-                        <li>
-                            <p>
-                                ${participant.name}
-                                <span class="to-pay">to pay $${expense.calculatedAmount / expense.notSettled.length}</span>
-                            </p>
-                            <button data-expense-id="${expense.id}" data-participant-id="${participant.id}" class="expense-paid-btn">Paid</button>
-                        </li>
-                    `
-                    )
-                    .join('')}
-            </ul>
-
-        `;
+    expenseParticipants: (expense: Expense): HTMLElement => {
+        const participantsList = document.createElement('ul');
+        participantsList.classList.add('expense-detail-participants');
+
+        const paidByItem = document.createElement('li');
+        const paidByText = document.createElement('p');
+        paidByText.innerHTML = `${expense.paidBy.name} <span class="to-receive">to receive &#x20b9;${expense.calculatedAmount.toFixed(2)}</span>`;
path: src/scripts/view/template.ts
user: login: Kaushal-SF
body: I know its bit annoying. However, we can avoid using `innerHTML` if we have better alternative to achieve this.
Same comment for other instances in this file.
created_at: 2025-04-13T16:49:44Z
author_association: COLLABORATOR
path: src/scripts/view/template.ts
user: login: Kaushal-SF
body: Is it worth providing a proper comment for what these functions are doing?
created_at: 2025-04-13T16:51:52Z
author_association: COLLABORATOR
path: src/scripts/view/template.ts
user: login: Kaushal-SF
body: Since we are doing too many things in the single file. I think we have a good opportunity achieve code modularity.
Advantages would be better readability and easy maintainability. Just my two cents.
created_at: 2025-04-13T16:55:50Z
author_association: COLLABORATOR
diff_hunk: @@ -1,68 +1,163 @@
-import type { Expense } from '../model/interfaces';
+import { Expense } from '../model/interfaces';
 
 export const templates = {
-    recentExpenseList: (expense: Expense) => {
-        return `
-            <li>
-                <p>${expense.title}</p>
-                <p>$${expense.calculatedAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')})</p>
-                <button class="view-details-button" popovertarget="expense-detail" data-expense-id="${expense.id}">View Details</button>
-            </li>
-        `;
+    recentExpenseList: (expense: Expense): HTMLElement => {
+        const listItem = document.createElement('li');
+
+        const title = document.createElement('p');
+        title.textContent = expense.title;
+
+        listItem.appendChild(title);
+
+        if (expense.calculatedAmount === 0) {
+            const message = document.createElement('p');
+            message.classList.add('payment-settle-message');
+            message.textContent = 'This expense has been successfully resolved';
+            listItem.appendChild(message);
+        } else {
+            const amount = document.createElement('p');
+            amount.innerHTML = templates.expenseSplitDisplay(expense);
+            listItem.appendChild(amount);
+        }
+
+        const buttonController = document.createElement('div');
+        buttonController.classList.add('button-controller');
+        listItem.appendChild(buttonController);
+
+        const viewDetailsButton = document.createElement('button');
+        viewDetailsButton.classList.add('view-details-button');
+        viewDetailsButton.setAttribute('data-expense-id', expense.id);
+        viewDetailsButton.setAttribute('popovertarget', 'expense-detail');
+        viewDetailsButton.textContent = 'View Details';
+        buttonController.appendChild(viewDetailsButton);
+
+        const deleteButtonIcon = document.createElement('img');
+        deleteButtonIcon.src = `${new URL('../../assets/icons/delete.svg', import.meta.url)}`;
+        deleteButtonIcon.alt = 'delete icon';
+        deleteButtonIcon.classList.add('delete-button-icon');
+
+        const deleteButton = document.createElement('button');
+        deleteButton.classList.add('expense-delete-button');
+        deleteButton.setAttribute('data-expense-id', expense.id);
+        deleteButton.appendChild(deleteButtonIcon);
+
+        buttonController.appendChild(deleteButton);
+
+        return listItem;
+    },
+
+    expenseSplitDisplay: (expense: Expense) => {
+        return `&#x20b9;${expense.originalAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')}, ${expense.settled.map((participant) => participant.name).join(', ')})`;
     },
 
-    expenseDetails: (expense: Expense) => {
-        return `
-            <p class="expense-detail-title">${expense.title}</p>
-            <p class="expense-detail-description">${expense.description}</p>
-            <p class="expense-detail-split">$${expense.calculatedAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')})</p>
-            ${templates.expenseParticipants(expense)}
-        `;
+    expenseDetails: (expense: Expense): HTMLElement => {
+        const container = document.createElement('div');
+
+        const title = document.createElement('p');
+        title.classList.add('expense-detail-title');
+        title.textContent = expense.title;
+
+        const closeIcon = document.createElement('img');
+        closeIcon.src = `${new URL('../../assets/icons/close.svg', import.meta.url)}`;
+        closeIcon.alt = 'close icon';
+
+        const closeButton = document.createElement('button');
+        closeButton.setAttribute('popovertarget', 'expense-detail');
+        closeButton.classList.add('view-detail-close-button');
+        closeButton.appendChild(closeIcon);
+
+        const titleDiv = document.createElement('div');
+        titleDiv.classList.add('view-detail-title-div');
+
+        titleDiv.appendChild(title);
+        titleDiv.appendChild(closeButton);
+        container.appendChild(titleDiv);
+
+        const description = document.createElement('p');
+        description.classList.add('expense-detail-description');
+        description.textContent = expense.description;
+        container.appendChild(description);
+
+        const splitAmount = document.createElement('p');
+        splitAmount.classList.add('expense-detail-split');
+        splitAmount.innerHTML = templates.expenseSplitDisplay(expense);
+        container.appendChild(splitAmount);
+
+        const participants = templates.expenseParticipants(expense);
+        container.appendChild(participants);
+
+        const editButton = document.createElement('button');
+        editButton.textContent = 'Edit';
+        editButton.classList.add('edit-button');
+        editButton.dataset.expenseId = expense.id;
+        editButton.setAttribute('popovertarget', 'form-container');
+
+        if (expense.settled.length == 0) {
+            container.appendChild(editButton);
+        }
+
+        return container;
     },
 
-    expenseParticipants: (expense: Expense) => {
-        return `
-            <ul class="expense-detail-participants">
-                <li>
-                    <P>
-                        ${expense.paidBy.name}
-                        <span class="to-receive">to receive $${expense.calculatedAmount}</span>
-                    </P>
-                </li>
-                ${expense.notSettled
-                    .map(
-                        (participant) => `
-                        <li>
-                            <p>
-                                ${participant.name}
-                                <span class="to-pay">to pay $${expense.calculatedAmount / expense.notSettled.length}</span>
-                            </p>
-                            <button data-expense-id="${expense.id}" data-participant-id="${participant.id}" class="expense-paid-btn">Paid</button>
-                        </li>
-                    `
-                    )
-                    .join('')}
-            </ul>
-
-        `;
+    expenseParticipants: (expense: Expense): HTMLElement => {
+        const participantsList = document.createElement('ul');
+        participantsList.classList.add('expense-detail-participants');
+
+        const paidByItem = document.createElement('li');
+        const paidByText = document.createElement('p');
+        paidByText.innerHTML = `${expense.paidBy.name} <span class="to-receive">to receive &#x20b9;${expense.calculatedAmount.toFixed(2)}</span>`;
path: src/scripts/view/template.ts
user: login: krushna-simform
body: Removed `innerHTML` from templates (https://github.com/dhruvdhaduk-simform/fair-share/pull/15/commits/01095989139efdd0987a0b950d2cc38a36d69633) 
created_at: 2025-04-14T09:02:08Z
author_association: COLLABORATOR
path: src/scripts/view/template.ts
user: login: krushna-simform
body: added comments for the function to explain what the function does (https://github.com/dhruvdhaduk-simform/fair-share/pull/15/commits/0849a6f6a20d0cf5dbc519bbff02bcad4f085421)

created_at: 2025-04-14T09:02:50Z
author_association: COLLABORATOR
path: src/scripts/view/template.ts
user: login: krushna-simform
body: For templates, I have already created different functions to render data in various parts. If I create separate utilities, I will need to create a utility for every function within the templates.
created_at: 2025-04-14T09:07:20Z
author_association: COLLABORATOR
path: src/scripts/view/template.ts
user: login: Kaushal-SF
body: Got it.
created_at: 2025-04-14T09:41:50Z
author_association: COLLABORATOR
diff_hunk: @@ -1,68 +1,184 @@
-import type { Expense } from '../model/interfaces';
+import { Expense } from '../model/interfaces';
 
 export const templates = {
-    recentExpenseList: (expense: Expense) => {
-        return `
-            <li>
-                <p>${expense.title}</p>
-                <p>$${expense.calculatedAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')})</p>
-                <button class="view-details-button" popovertarget="expense-detail" data-expense-id="${expense.id}">View Details</button>
-            </li>
-        `;
+    /* This function serves as a template for displaying the list of recent expenses */
+    recentExpenseList: (expense: Expense): HTMLElement => {
+        const listItem = document.createElement('li');
+
+        const title = document.createElement('p');
+        title.textContent = expense.title;
+
+        listItem.appendChild(title);
+
+        if (expense.calculatedAmount === 0) {
+            const message = document.createElement('p');
+            message.classList.add('payment-settle-message');
+            message.textContent = 'This expense has been successfully resolved';
+            listItem.appendChild(message);
+        } else {
+            const amount = document.createElement('p');
+            amount.textContent = templates.expenseSplitDisplay(expense);
+            listItem.appendChild(amount);
+        }
+
+        const buttonController = document.createElement('div');
+        buttonController.classList.add('button-controller');
+        listItem.appendChild(buttonController);
+
+        const viewDetailsButton = document.createElement('button');
+        viewDetailsButton.classList.add('view-details-button');
+        viewDetailsButton.setAttribute('data-expense-id', expense.id);
+        viewDetailsButton.setAttribute('popovertarget', 'expense-detail');
+        viewDetailsButton.textContent = 'View Details';
+        buttonController.appendChild(viewDetailsButton);
+
+        const deleteButtonIcon = document.createElement('img');
+        deleteButtonIcon.src = `${new URL('../../assets/icons/delete.svg', import.meta.url)}`;
+        deleteButtonIcon.alt = 'delete icon';
+        deleteButtonIcon.classList.add('delete-button-icon');
+
+        const deleteButton = document.createElement('button');
+        deleteButton.classList.add('expense-delete-button');
+        deleteButton.setAttribute('data-expense-id', expense.id);
+        deleteButton.appendChild(deleteButtonIcon);
+
+        buttonController.appendChild(deleteButton);
+
+        return listItem;
     },
 
-    expenseDetails: (expense: Expense) => {
-        return `
-            <p class="expense-detail-title">${expense.title}</p>
-            <p class="expense-detail-description">${expense.description}</p>
-            <p class="expense-detail-split">$${expense.calculatedAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')})</p>
-            ${templates.expenseParticipants(expense)}
-        `;
+    /* This function serves as a template that provides information about expenses paid by someone and how they are split with participants */
+    expenseSplitDisplay: (expense: Expense) => {
+        return `₹${expense.originalAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')}, ${expense.settled.map((participant) => participant.name).join(', ')})`;
path: src/scripts/view/template.ts
user: login: Kaushal-SF
body: @krushna-simform Can we use proper entity code for `rupee` symbol?
created_at: 2025-04-14T09:49:37Z
author_association: COLLABORATOR
diff_hunk: @@ -1,68 +1,184 @@
-import type { Expense } from '../model/interfaces';
+import { Expense } from '../model/interfaces';
 
 export const templates = {
-    recentExpenseList: (expense: Expense) => {
-        return `
-            <li>
-                <p>${expense.title}</p>
-                <p>$${expense.calculatedAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')})</p>
-                <button class="view-details-button" popovertarget="expense-detail" data-expense-id="${expense.id}">View Details</button>
-            </li>
-        `;
+    /* This function serves as a template for displaying the list of recent expenses */
+    recentExpenseList: (expense: Expense): HTMLElement => {
+        const listItem = document.createElement('li');
+
+        const title = document.createElement('p');
+        title.textContent = expense.title;
+
+        listItem.appendChild(title);
+
+        if (expense.calculatedAmount === 0) {
+            const message = document.createElement('p');
+            message.classList.add('payment-settle-message');
+            message.textContent = 'This expense has been successfully resolved';
+            listItem.appendChild(message);
+        } else {
+            const amount = document.createElement('p');
+            amount.textContent = templates.expenseSplitDisplay(expense);
+            listItem.appendChild(amount);
+        }
+
+        const buttonController = document.createElement('div');
+        buttonController.classList.add('button-controller');
+        listItem.appendChild(buttonController);
+
+        const viewDetailsButton = document.createElement('button');
+        viewDetailsButton.classList.add('view-details-button');
+        viewDetailsButton.setAttribute('data-expense-id', expense.id);
+        viewDetailsButton.setAttribute('popovertarget', 'expense-detail');
+        viewDetailsButton.textContent = 'View Details';
+        buttonController.appendChild(viewDetailsButton);
+
+        const deleteButtonIcon = document.createElement('img');
+        deleteButtonIcon.src = `${new URL('../../assets/icons/delete.svg', import.meta.url)}`;
+        deleteButtonIcon.alt = 'delete icon';
+        deleteButtonIcon.classList.add('delete-button-icon');
+
+        const deleteButton = document.createElement('button');
+        deleteButton.classList.add('expense-delete-button');
+        deleteButton.setAttribute('data-expense-id', expense.id);
+        deleteButton.appendChild(deleteButtonIcon);
+
+        buttonController.appendChild(deleteButton);
+
+        return listItem;
     },
 
-    expenseDetails: (expense: Expense) => {
-        return `
-            <p class="expense-detail-title">${expense.title}</p>
-            <p class="expense-detail-description">${expense.description}</p>
-            <p class="expense-detail-split">$${expense.calculatedAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')})</p>
-            ${templates.expenseParticipants(expense)}
-        `;
+    /* This function serves as a template that provides information about expenses paid by someone and how they are split with participants */
+    expenseSplitDisplay: (expense: Expense) => {
+        return `₹${expense.originalAmount} (${expense.paidBy.name} paid, split with ${expense.notSettled.map((participant) => participant.name).join(', ')}, ${expense.settled.map((participant) => participant.name).join(', ')})`;
path: src/scripts/view/template.ts
user: login: krushna-simform
body: > @krushna-simform Can we use proper entity code for `rupee` symbol?

Added proper code for rupee sign (https://github.com/dhruvdhaduk-simform/fair-share/pull/15/commits/1adb970b2d9715874029a41cf37b01973505dd8e)
created_at: 2025-04-14T10:39:29Z
author_association: COLLABORATOR

PR comments:
user: login: vercel[bot]
created_at: 2025-04-10T12:10:13Z
body: @krushna-simform is attempting to deploy a commit to the **Dhruv Dhaduk's projects** Team on [Vercel](https://vercel.com).

A member of the Team first needs to [authorize it](https://vercel.com/git/authorize?team=Dhruv%20Dhaduk's%20projects&type=github&job=%7B%22headInfo%22%3A%7B%22sha%22%3A%22f60e990fa09695678ed90ad4b43935629feeea34%22%7D%2C%22id%22%3A%22QmUMFvhnAhTgjnDyyzi9C6JQ24ZhxnSxwhhRE4pZDgXJfZ%22%2C%22org%22%3A%22dhruvdhaduk-simform%22%2C%22prId%22%3A15%2C%22repo%22%3A%22fair-share%22%7D).


author_association: NONE
user: login: vercel[bot]
created_at: 2025-04-10T12:11:04Z
body: [vc]: #DkHymx2+V3AoutNdj5dp/p9K9Ef2LItubmUXOb3XoWs=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJmYWlyLXNoYXJlIiwicm9vdERpcmVjdG9yeSI6bnVsbCwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2RocnV2LWRoYWR1a3MtcHJvamVjdHMtNmU2NmM4MTUvZmFpci1zaGFyZS80dmVpNnR1eU42dkxCWGd6VnU3Ykdvenc3RUhOIiwicHJldmlld1VybCI6ImZhaXItc2hhcmUtZ2l0LWZvcmsta3J1cy0zYmU4MTYtZGhydXYtZGhhZHVrcy1wcm9qZWN0cy02ZTY2YzgxNS52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6ImZhaXItc2hhcmUtZ2l0LWZvcmsta3J1cy0zYmU4MTYtZGhydXYtZGhhZHVrcy1wcm9qZWN0cy02ZTY2YzgxNS52ZXJjZWwuYXBwIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **fair-share** | ✅ Ready ([Inspect](https://vercel.com/dhruv-dhaduks-projects-6e66c815/fair-share/4vei6tuyN6vLBXgzVu7bGozw7EHN)) | [Visit Preview](https://fair-share-git-fork-krus-3be816-dhruv-dhaduks-projects-6e66c815.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/fair-share-git-fork-krus-3be816-dhruv-dhaduks-projects-6e66c815.vercel.app?via=pr-comment-feedback-link) | Apr 14, 2025 11:05am |


author_association: NONE
user: login: Kaushal-SF
created_at: 2025-04-14T09:45:10Z
body: @krushna-simform when there is no recent expense available, we can add proper message.

<img width="520" alt="image" src="https://github.com/user-attachments/assets/db306dff-a71e-43c3-b079-fa7abd15d354" />

author_association: COLLABORATOR
user: login: Kaushal-SF
created_at: 2025-04-14T09:47:22Z
body: We can change color for the close icon to make it visible properly without any hassle.

<img width="304" alt="image" src="https://github.com/user-attachments/assets/88f3de54-0a65-4cbd-bd14-3edcfd391862" />

author_association: COLLABORATOR
user: login: krushna-simform
created_at: 2025-04-14T10:38:10Z
body: > @krushna-simform when there is no recent expense available, we can add proper message.
> 
> <img alt="image" width="520" src="https://private-user-images.githubusercontent.com/97082607/433316301-db306dff-a71e-43c3-b079-fa7abd15d354.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDQ2MjQ1MDMsIm5iZiI6MTc0NDYyNDIwMywicGF0aCI6Ii85NzA4MjYwNy80MzMzMTYzMDEtZGIzMDZkZmYtYTcxZS00M2MzLWIwNzktZmE3YWJkMTVkMzU0LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA0MTQlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNDE0VDA5NTAwM1omWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTViNzYzYzkxYjFmNTM3MmU0OTE5NjQ3NzY5NjUwNzAwMDY2N2VkMmYxYmQ2NTNjOGY3NjU4Mzc0Y2E0MDZiYzUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.EMT-dGkYUO4g8FJjjGtwK5KuvOagFi9w6OoM32OZZ8I">

Added proper message when there is no recent expense available (https://github.com/dhruvdhaduk-simform/fair-share/pull/15/commits/80607390df54b9e7c0b9486446f6675e3c2344f4)
author_association: COLLABORATOR
user: login: krushna-simform
created_at: 2025-04-14T10:38:53Z
body: > We can change color for the close icon to make it visible properly without any hassle.
> 
> <img alt="image" width="304" src="https://private-user-images.githubusercontent.com/97082607/433317513-88f3de54-0a65-4cbd-bd14-3edcfd391862.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDQ2MjQ1MDMsIm5iZiI6MTc0NDYyNDIwMywicGF0aCI6Ii85NzA4MjYwNy80MzMzMTc1MTMtODhmM2RlNTQtMGE2NS00Y2JkLWJkMTQtM2VkY2ZkMzkxODYyLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA0MTQlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNDE0VDA5NTAwM1omWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTQ0ZTI4ZWQzNzQyNTA0NmRhODI2NTY5NDEwMzUzZGEyZTJmMDgzMTIzOGEwNWNkZTJlNDIxYTE4YzJiMzE2ZDImWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.pc5GlWNNulhl-mWrTYFkdjG5Z9b-iDkjtdiPQGuIrmU">

Changed close button color (https://github.com/dhruvdhaduk-simform/fair-share/pull/15/commits/0f1c5589039b9627bb00e49ee6ca3cefe3865044)
author_association: COLLABORATOR
user: login: Kaushal-SF
created_at: 2025-04-14T10:57:47Z
body: Minor Nit: Extra `,` at the end.

<img width="294" alt="image" src="https://github.com/user-attachments/assets/fb0c8bc2-ca96-40df-b2ab-ca6d36d928a4" />
<img width="542" alt="image" src="https://github.com/user-attachments/assets/d259ce38-57a4-478c-bffc-85ab1f5b0462" />

author_association: COLLABORATOR
user: login: krushna-simform
created_at: 2025-04-14T11:06:53Z
body: > Minor Nit: Extra `,` at the end.
> 
> <img alt="image" width="294" src="https://private-user-images.githubusercontent.com/97082607/433342239-fb0c8bc2-ca96-40df-b2ab-ca6d36d928a4.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDQ2Mjg5MDIsIm5iZiI6MTc0NDYyODYwMiwicGF0aCI6Ii85NzA4MjYwNy80MzMzNDIyMzktZmIwYzhiYzItY2E5Ni00MGRmLWIyYWItY2E2ZDM2ZDkyOGE0LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA0MTQlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNDE0VDExMDMyMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWQ3ODc3NjY4ZWQxNTZmZDg3MDMwMzc1YWE2OGMxMGE2ODlkZjE3NzA5MjNjNTU4Njg0YTFjNzA1ZGRiNTRhNjQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.kJU6cKF1tYxyZV-49E8uM3JnCjAO6zeo9u33jrByn0Q"> <img alt="image" width="542" src="https://private-user-images.githubusercontent.com/97082607/433341772-d259ce38-57a4-478c-bffc-85ab1f5b0462.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDQ2Mjg5MDIsIm5iZiI6MTc0NDYyODYwMiwicGF0aCI6Ii85NzA4MjYwNy80MzMzNDE3NzItZDI1OWNlMzgtNTdhNC00NzhjLWJmZmMtODVhYjFmNWIwNDYyLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA0MTQlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNDE0VDExMDMyMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTkxM2ZkYmYwNjg1ZTcxMzFiZDg4ZThmMDBhNmRiODI3NzNlZjI5ZDIwYTExMDVlOTg3NWE3Zjc2NjQ5ZWRmODAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.M3RJLeMKtNpfRaraHWpyXUZ9Va0sQ-58m3kPugdaE1Y">

Removed extra `,` (https://github.com/dhruvdhaduk-simform/fair-share/pull/15/commits/824f341ee5a028bb9fc1ba64897a062c7162cba8)
author_association: COLLABORATOR