Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
aa65db1
test : Completed single filters
Jul 23, 2025
d16c780
fix : TC47 flaky, commented TC117 due to bug
Jul 24, 2025
409220f
fix : updated code for TC47 and removed unused function
Jul 24, 2025
3bf0fc9
fix : removed unused function
Jul 24, 2025
97687cf
fix : code cleanup
Jul 24, 2025
c758fcf
test : completed project related tests
Jul 24, 2025
8d79221
Added tests for TC112 and TC113
jayson31 Jul 25, 2025
06fc7aa
Fixed TC113
jayson31 Jul 25, 2025
d03d106
Updated test case name to avoid conflict
jayson31 Jul 28, 2025
11dd884
Merge branch 'develop' into test/automate/project-tab
Jul 30, 2025
355f9a4
Updated test name
jayson31 Jul 30, 2025
b23c007
all passed.
Jul 30, 2025
c35b584
fix : updated TC3 from Thu to Fri
Jul 30, 2025
f0c7c46
fix : remove .only and lint issues
Jul 30, 2025
427d31e
Merge pull request #701 from rtCamp/test/automate/TC112-TC113
jayson31 Jul 31, 2025
2e24689
Fix tag overflow isue in project-page
Siddhantsingh1230 Jul 31, 2025
a44f93f
Add cellref to last row to fix vertical scroll
Siddhantsingh1230 Jul 31, 2025
44892b1
Added readme file for visual regression tests
jayson31 Jul 31, 2025
0de0213
Fix tag layout
Siddhantsingh1230 Jul 31, 2025
2475f6d
Merge pull request #704 from rtCamp/Update-visual-test-readme
jayson31 Jul 31, 2025
b7d4a7b
Update currencyFormat utility for better currency format
Siddhantsingh1230 Jul 31, 2025
958bcd2
Remove ring-offset from currency input
Siddhantsingh1230 Jul 31, 2025
82dfffe
Merge branch 'develop' into test/automate/project-tab
Jul 31, 2025
da502aa
fix : resource allocation errros
Jul 31, 2025
e4a1da1
fix : lint issues
Jul 31, 2025
7ab2313
Merge pull request #699 from rtCamp/test/automate/project-tab
maharrshi Jul 31, 2025
74aa5ed
Merge pull request #706 from rtCamp/fix/project-vertical-scroll
Siddhantsingh1230 Aug 4, 2025
4e0e138
fix: Update badge colors to match the UI
niraj2477 Aug 4, 2025
9f47bfd
refactor: Update get task method to priotize recent worked on task i…
niraj2477 Aug 4, 2025
54ad156
chore: Resolve linting issue
niraj2477 Aug 4, 2025
7dac334
Merge pull request #709 from rtCamp/fix/processing-status
niraj2477 Aug 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
exclude: 'node_modules|.git'
default_stages: [commit]
default_stages: [pre-commit]
fail_fast: false

repos:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ const AddTime = ({
search: searchTask,
projects: selectedProject,
page_length: 100,
filter_recent: true,
})
.then((res) => {
setTask(res.message.task);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,10 @@ const getFieldComponent = (
<Input
type="text"
step="any"
value={Number(value).toFixed(2)}
value={value}
onChange={(e) => handleChange(parseFloat(e.target.value) || 0)}
disabled={isReadOnly}
className="!border-none rounded-none focus-visible:outline-none h-auto focus-visible:ring-0 !px-0 text-sm !py-1"
className="!border-none rounded-none focus-visible:outline-none h-auto focus-visible:ring-0 !px-0 text-sm !py-1 focus-visible:ring-offset-0"
/>
</div>
);
Expand Down
44 changes: 31 additions & 13 deletions frontend/packages/app/src/app/components/list-view/dataCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,21 +116,30 @@ export const DataCell = ({ meta, title_field, docType, row, value, currency }: D
if (SUCCESS_SELECT_VALUES.includes(value)) {
if (!value) return null;
return (
<Badge className="truncate" variant="success">
<Badge
className={mergeClassNames("truncate", "bg-success/20 text-success hover:bg-success/20")}
variant="success"
>
{value}
</Badge>
);
} else if (FAIL_SELECT_VALUES.includes(value)) {
if (!value) return null;
return (
<Badge className="truncate" variant="destructive">
<Badge
className={mergeClassNames("truncate", "bg-destructive/20 text-destructive hover:bg-destructive/20")}
variant="destructive"
>
{value}
</Badge>
);
} else if (OPEN_SELECT_VALUES.includes(value)) {
if (!value) return null;
return (
<Badge className="truncate" variant="warning">
<Badge
className={mergeClassNames("truncate", "bg-warning/20 text-warning hover:bg-warning/20")}
variant="warning"
>
{value}
</Badge>
);
Expand All @@ -146,7 +155,16 @@ export const DataCell = ({ meta, title_field, docType, row, value, currency }: D
const val = Number(value);

return (
<Badge className="truncate" variant={val === 1 ? "success" : "destructive"} title={value}>
<Badge
className={mergeClassNames(
"truncate",
val === 1
? "bg-success/20 text-success hover:bg-success/20"
: "bg-destructive/20 text-destructive hover:bg-destructive/20"
)}
variant={val === 1 ? "success" : "destructive"}
title={value}
>
{val === 1 ? "Yes" : "No"}
</Badge>
);
Expand Down Expand Up @@ -174,15 +192,15 @@ const progressBarColor = (value: number) => {

const selectBadgeColor = (value: string, options: Array<string>) => {
const colors = [
"bg-green-600",
"bg-blue-600",
"bg-slate-600",
"bg-purple-600",
"bg-teal-600",
"bg-orange-600",
"bg-zinc-600",
"bg-yellow-600",
"bg-gray-600",
"bg-success/20 text-success",
"bg-blue-600/20 text-blue-900 dark:bg-primary/70",
"bg-slate-600/20 text-slate-900 dark:bg-primary/70",
"bg-purple-600/20 text-purple-900 dark:bg-primary/70",
"bg-teal-600/20 text-teal-900 dark:bg-primary/70",
"bg-orange-600/20 text-orange-900 dark:bg-primary/70",
"bg-zinc-600/20 text-zinc-900 dark:bg-primary/70",
"bg-yellow-600/20 text-yellow-900 dark:bg-primary/70",
"bg-gray-600/20 text-gray-900 dark:bg-primary/70",
];

const colorMap = options.reduce((acc, option, index) => {
Expand Down
5 changes: 3 additions & 2 deletions frontend/packages/app/src/app/components/tags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,12 @@ const Tags: React.FC<TagsProps> = ({ userTags, doctype, docname, className, muta
key={tag}
aria-disabled={loading}
className={mergeClassNames(
"px-2 rounded-full bg-muted text-foreground hover:bg-muted/80 py-1 text-xs flex items-center gap-1",
"px-2 rounded-full bg-muted text-foreground hover:bg-muted/80 py-1 text-xs flex items-center gap-1 truncate",
loading && "opacity-50 pointer-events-none"
)}
>
{tag} <X className="w-3 h-3 cursor-pointer" onClick={() => handleRemoveTag(tag)} />
<p className="flex-shrink truncate">{tag}</p>{" "}
<X className="w-3 h-3 flex-shrink-0 flex-grow cursor-pointer" onClick={() => handleRemoveTag(tag)} />
</Badge>
))}
</div>
Expand Down
22 changes: 18 additions & 4 deletions frontend/packages/app/src/app/pages/project/components/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,14 @@ export const getColumnInfo = (
} else if (meta.fieldname === "status") {
return (
<Badge
className="truncate"
className={mergeClassNames(
"truncate",
value === "Open"
? "bg-warning/20 text-warning hover:bg-warning/20"
: value === "Completed"
? "bg-success/20 text-success hover:bg-success/20"
: "bg-destructive/20 text-destructive hover:bg-destructive/20"
)}
variant={value === "Open" ? "warning" : value === "Completed" ? "success" : "destructive"}
>
{value}
Expand All @@ -90,7 +97,14 @@ export const getColumnInfo = (
} else if (meta.fieldname === "priority") {
return (
<Badge
className="truncate"
className={mergeClassNames(
"truncate",
value === "Low"
? "bg-success/20 text-success hover:bg-success/20"
: value === "Medium"
? "bg-warning/20 text-warning hover:bg-warning/20"
: "bg-destructive/20 text-destructive hover:bg-destructive/20"
)}
variant={value === "Low" ? "success" : value === "Medium" ? "warning" : "destructive"}
>
{value}
Expand Down Expand Up @@ -144,10 +158,10 @@ export const getColumnInfo = (
{tags.map((tag) => (
<Badge
key={tag}
className="cursor-pointer hover:bg-slate-300/80 bg-slate-200 dark:bg-gray-800 dark:hover:bg-gray-700"
className="cursor-pointer hover:bg-slate-300/80 bg-slate-200 dark:bg-gray-800 dark:hover:bg-gray-700 max-w-26"
variant="secondary"
>
{tag}
<p className="flex-shrink truncate">{tag}</p>
</Badge>
))}
</div>
Expand Down
11 changes: 3 additions & 8 deletions frontend/packages/app/src/app/pages/project/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,12 +314,8 @@ const ProjectTable = ({ viewData, meta }: ProjectProps) => {
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow className="px-3" key={row.id} data-state={row.getIsSelected() && "selected"}>
{row.getVisibleCells().map((cell, cellIndex) => {
{row.getVisibleCells().map((cell) => {
const isPinned = cell.column.getIsPinned() === "left";
// Attach ref to the first unpinned cell for infinite scroll
const visibleCells = row.getVisibleCells();
const firstUnpinnedIndex = visibleCells.findIndex((c) => c.column.getIsPinned() !== "left");
const needToAddRef = projectState.hasMore && cellIndex === firstUnpinnedIndex && !isPinned;

return (
<TableCell
Expand All @@ -334,7 +330,6 @@ const ProjectTable = ({ viewData, meta }: ProjectProps) => {
left: isPinned ? cell.column.getStart("left") : undefined,
zIndex: isPinned ? 1 : undefined,
}}
ref={needToAddRef ? cellRef : null}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
Expand All @@ -351,8 +346,8 @@ const ProjectTable = ({ viewData, meta }: ProjectProps) => {
)}

{projectState?.data && projectState.hasMore && (
<TableRow className="p-0">
<TableCell colSpan={viewData.rows.length} className="text-center p-0">
<TableRow className="p-0 w-full !px-0" ref={cellRef}>
<TableCell colSpan={viewData.rows.length + 1} className="text-center w-full !py-0">
<Skeleton className="h-10 w-full" />
</TableCell>
</TableRow>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const getColumn = (
}}
title={String(value ?? "")}
variant="p"
className="max-w-sm py-1 truncate cursor-pointer hover:underline"
className=" py-1 truncate cursor-pointer hover:underline"
>
{value}
</Typography>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { TaskData } from "@/types";

export const TaskPriority = ({ priority }: { priority: TaskData["priority"] }) => {
const priorityCss = {
Low: "bg-slate-200 text-slate-900 hover:bg-slate-200",
Low: "bg-slate-200/60 text-slate-900 hover:bg-slate-200",
Medium: "bg-warning/20 text-warning hover:bg-warning/20",
High: "bg-orange-200 text-orange-900 hover:bg-orange-200",
Urgent: "bg-destructive/20 text-destructive hover:bg-destructive/20",
Expand Down
44 changes: 40 additions & 4 deletions frontend/packages/app/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,47 @@ export const canCreate = (doctype: string) => {
return window.frappe?.boot?.user?.can_create?.includes(doctype) ?? true;
};

export const currencyFormat = (currency: string) => {
return new Intl.NumberFormat("en-IN", {
export const currencyFormat = (currency: string = "INR"): Intl.NumberFormat => {
// Currency-specific locale settings
const localeSettings: Record<string, string> = {
AED: "ar-AE", // UAE Dirham
AUD: "en-AU", // Australian Dollar
CHF: "de-CH", // Swiss Franc
CNY: "zh-CN", // Chinese Yuan
EUR: "de-DE", // Euro (German format)
GBP: "en-GB", // British Pound
INR: "en-IN", // Indian Rupee
JPY: "ja-JP", // Japanese Yen
USD: "en-US", // US Dollar
};

const locale = localeSettings[currency] || "en-IN";

const options: Intl.NumberFormatOptions = {
style: "currency",
currency: currency,
});
currency: currency || "INR",
currencyDisplay: "symbol",
};

const zeroDecimalCurrencies = ["JPY", "KRW", "VND", "IDR"];
if (zeroDecimalCurrencies.includes(currency)) {
options.maximumFractionDigits = 0;
options.minimumFractionDigits = 0;
} else {
// Default to 2 decimal places
options.minimumFractionDigits = 2;
options.maximumFractionDigits = 2;
}

try {
return new Intl.NumberFormat(locale, options);
} catch (e) {
console.error(`Error formatting currency (${currency}):`, e);
return new Intl.NumberFormat("en-IN", {
style: "currency",
currency: currency || "INR",
});
}
};

export const getBgCsssForToday = (date: string) => {
Expand Down
54 changes: 40 additions & 14 deletions next_pms/timesheet/api/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import frappe
from frappe.query_builder import Case, DocType
from frappe.query_builder import functions as fn
from frappe.utils import getdate
from frappe.utils import add_days, getdate, now_datetime
from pypika import Criterion, Order

from . import get_count
Expand All @@ -17,6 +17,7 @@ def get_task_list(
projects=None,
status: list | str = None,
fields: list | str = None,
filter_recent: bool = False,
):
import json

Expand Down Expand Up @@ -90,21 +91,37 @@ def get_task_list(
if status:
tasks = tasks.where(doctype.status.isin(status))
filter.update({"status": ["in", status]})

if page_length:
tasks = tasks.limit(page_length)

# We need to order the tasks based on the user's liked tasks.
tasks = (
tasks.offset(start).orderby(
Case()
.when(
fn.Function("INSTR", doctype._liked_by, f'"{frappe.session.user}"') > 0,
1,
)
.else_(0),
order=Order.desc,
order_conditions = []

# If filter_recent is True, we will order the tasks based on the recent worked tasks First.
# This will help in showing the tasks that the user has worked on recently at the top.
if filter_recent:
recent_worked_tasks = get_recent_log_tasks()
order_conditions = []
if recent_worked_tasks:
order_conditions.append(Case().when(doctype.name.isin(recent_worked_tasks), 1).else_(0))

order_conditions.append(
Case()
.when(
fn.Function("INSTR", doctype._liked_by, f'"{frappe.session.user}"') > 0,
1,
)
).run(as_dict=True)
.else_(0)
)
# Since we can not hide closed tasks, as user might need to add time against it,
# We can deprioritize the closed tasks.
order_conditions.append(Case().when(doctype.status.isin(["Open", "Working"]), 1).else_(0))

tasks = tasks.offset(start).orderby(
*order_conditions,
order=Order.desc,
)
tasks = tasks.run(as_dict=True)

count = get_count(
doctype="Task",
Expand Down Expand Up @@ -180,8 +197,6 @@ def get_task(task: str, start_date: str | datetime.date, end_date: str | datetim
"status": task.status,
"worked_by": result,
"name": task.name,
# Check if task DocType has custom_github_issue_link field
# If yes, then create github link else it will be blank.
"gh_link": task.custom_github_issue_link if task.meta.has_field("custom_github_issue_link") else "",
}

Expand Down Expand Up @@ -254,3 +269,14 @@ def get_liked_tasks():
from next_pms.timesheet.api.app import get_liked_documents

return get_liked_documents("Task", fields=["project.project_name"])


def get_recent_log_tasks():
return frappe.get_all(
"Timesheet Detail",
filters={
"owner": frappe.session.user,
"creation": [">=", add_days(now_datetime(), -7)],
},
pluck="task",
)
2 changes: 1 addition & 1 deletion tests/e2e/data/employee/timesheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ module.exports = {
notification: "New Timesheet created successfully.",
cell: {
rowName: "Task for TC03",
col: "Thu",
col: "Fri",
},
payloadCreateProject: {
project_name: "TC03 Project",
Expand Down
Loading
Loading