Skip to content

Commit 37aab5d

Browse files
committed
chore: address comments
1 parent d2398a5 commit 37aab5d

File tree

7 files changed

+273
-30
lines changed

7 files changed

+273
-30
lines changed

examples/kendo-react-freemium/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Tasks from "./pages/Tasks";
88
import Settings from "./pages/Settings";
99
import Team from "./pages/Team";
1010
import Task from "./pages/Task";
11+
import NewTask from "./pages/NewTask";
1112

1213
export default function App() {
1314
return (
@@ -20,6 +21,7 @@ export default function App() {
2021
<Route path="/projects" element={<Projects />} />
2122
<Route path="/tasks" element={<Tasks />} />
2223
<Route path="/tasks/:taskId" element={<Task />} />
24+
<Route path="/tasks/new" element={<NewTask />} />
2325
<Route path="/team-management" element={<TeamManagement />} />
2426
<Route path="/team-management/:teamId" element={<Team />} />
2527
<Route path="/settings" element={<Settings />} />

examples/kendo-react-freemium/src/pages/Home.tsx

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { Calendar } from "@progress/kendo-react-dateinputs";
77
import { ListView, ListViewItemWrapper } from "@progress/kendo-react-listview";
88
import { pencilIcon, plusIcon, trashIcon } from "@progress/kendo-svg-icons";
99
import { tasksData, listData, projectsData, teamsData } from "./data";
10-
import React from "react";
1110
import { useNavigate } from "react-router-dom";
1211

1312
const ListItemRender = (props: any) => {
@@ -24,27 +23,18 @@ const ListItemRender = (props: any) => {
2423

2524
export default function Home() {
2625
const navigate = useNavigate();
27-
const [showAllProjects, setShowAllProjects] = React.useState(false);
28-
const [showAllTeams, setShowAllTeams] = React.useState(false);
29-
const [showAllTasks, setShowAllTasks] = React.useState(false);
30-
3126
const handleSeeAllProjectsClick = () => {
32-
setShowAllProjects(!showAllProjects);
27+
navigate('/projects');
3328
};
3429

3530
const handleSeeAllTeamsClick = () => {
36-
setShowAllTeams(!showAllTeams);
31+
navigate('/team-management');
3732
};
3833

3934
const handleSeeAllTasksClick = () => {
40-
setShowAllTasks(!showAllTasks);
35+
navigate('/tasks');
4136
}
4237

43-
const projectsToShow = showAllProjects ? projectsData : projectsData.slice(0, 5);
44-
const teamsToShow = showAllTeams ? teamsData : teamsData.slice(0, 4);
45-
46-
const tasksToShow = showAllTasks ? tasksData : tasksData.slice(0, 5);
47-
4838
return (
4939
<>
5040
<div style={{ minHeight: 'calc(100vh - 106px)'}} className="bg-linear-[119deg,_#F8F9FF_-1.78%,_#F3F2FF_47.75%,_#E6F5FF_97.28%] p-10">
@@ -57,8 +47,8 @@ export default function Home() {
5747
</CardHeader>
5848
<CardBody>
5949
<div className="grid grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-2">
60-
{projectsToShow.map((project, index) => {
61-
return <Card key={'project-' + index}>
50+
{projectsData.slice(0, 5).map((project, index) => {
51+
return <Card key={'project-' + index} className={`${index === 3 || index === 4 ? 'hidden 2xl:flex' : ''}`}>
6252
<CardBody className="flex flex-col justify-between">
6353
<div onClick={() => navigate('/projects')} className="cursor-pointer">
6454
<CardTitle className="font-medium line-clamp-5 h-25.5">{project.ProjectName}</CardTitle>
@@ -80,7 +70,7 @@ export default function Home() {
8070
</div>
8171
</CardBody>
8272
<CardFooter className="border-0 p-2">
83-
<Button fillMode="flat" themeColor="primary" onClick={handleSeeAllProjectsClick}>{!showAllProjects ? 'See all' : 'Collapse'}</Button>
73+
<Button fillMode="flat" themeColor="primary" onClick={handleSeeAllProjectsClick}>See all</Button>
8474
</CardFooter>
8575
</Card>
8676
</div>
@@ -93,7 +83,7 @@ export default function Home() {
9383
<CardTitle className="font-medium">Teams</CardTitle>
9484
</CardHeader>
9585
<CardBody className="flex flex-col gap-2">
96-
{teamsToShow.map((team, index) => {
86+
{teamsData.slice(0, 4).map((team, index) => {
9787
return <Card key={'team-' + index}>
9888
<div onClick={() => navigate('/team-management')} className="cursor-pointer">
9989
<CardBody className="flex items-center">
@@ -108,7 +98,7 @@ export default function Home() {
10898
})}
10999
</CardBody>
110100
<CardFooter className="border-0 p-2">
111-
<Button fillMode="flat" themeColor="primary" onClick={handleSeeAllTeamsClick}>{!showAllTeams ? 'See all' : 'Collapse'}</Button>
101+
<Button fillMode="flat" themeColor="primary" onClick={handleSeeAllTeamsClick}>See all</Button>
112102
</CardFooter>
113103
</Card>
114104
</div>
@@ -129,13 +119,13 @@ export default function Home() {
129119
<CardTitle className="font-medium">Tasks</CardTitle>
130120
</CardHeader>
131121
<CardBody className="h-full overflow-y-hidden">
132-
<Grid className="h-full" data={tasksToShow}>
122+
<Grid className="h-full" data={tasksData}>
133123
<GridColumn field="taskName" title="Task Name" />
134124
<GridColumn field="status" title="Status" width={115} />
135125
</Grid>
136126
</CardBody>
137127
<CardFooter className="border-0 p-2">
138-
<Button fillMode="flat" themeColor="primary" onClick={handleSeeAllTasksClick}>{!showAllTasks ? 'See all' : 'Collapse'}</Button>
128+
<Button fillMode="flat" themeColor="primary" onClick={handleSeeAllTasksClick}>See all</Button>
139129
</CardFooter>
140130
</Card>
141131
</div>
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
import { useParams } from "react-router";
2+
import { useNavigate } from "react-router-dom";
3+
import { priorities, projectManagers, projectsData, tasksData, taskTags } from "./data";
4+
import { Button } from "@progress/kendo-react-buttons";
5+
import { Avatar, Breadcrumb, BreadcrumbLinkMouseEvent, ExpansionPanel, ExpansionPanelContent } from "@progress/kendo-react-layout";
6+
import { homeIcon, folderIcon, trashIcon, checkIcon } from "@progress/kendo-svg-icons";
7+
import { SvgIcon } from "@progress/kendo-react-common";
8+
import { Reveal } from '@progress/kendo-react-animation';
9+
import React, { ReactElement } from "react";
10+
import { TextArea } from "@progress/kendo-react-inputs";
11+
import { DropDownList, ListItemProps, MultiSelect, TagData } from "@progress/kendo-react-dropdowns";
12+
import { DateInput } from "@progress/kendo-react-dateinputs";
13+
import { FloatingLabel } from "@progress/kendo-react-labels";
14+
import { Badge } from "@progress/kendo-react-indicators";
15+
16+
interface DataModel {
17+
id: string;
18+
text?: string;
19+
icon?: React.ReactNode;
20+
}
21+
22+
export default function Task() {
23+
let params = useParams();
24+
const navigate = useNavigate();
25+
const [projExpanded, setProjExpanded] = React.useState(true);
26+
const [dateExpanded, setDateExpanded] = React.useState(true);
27+
const [assigneeExpanded, setAssigneeExpanded] = React.useState(true);
28+
const [priorityExpanded, setPriorityExpanded] = React.useState(true);
29+
const [statusExpanded, setStatusExpanded] = React.useState(true);
30+
const [tagsExpanded, setTagsExpanded] = React.useState(true);
31+
const [project, setProject] = React.useState<string>();
32+
const [assignee, setAssignee] = React.useState<string[]>();
33+
const [dueDate, setDueDate] = React.useState<Date>();
34+
const [priority, setPriority] = React.useState<string>();
35+
const [status, setStatus] = React.useState<string>();
36+
const [tag, setTag] = React.useState<string[]>();
37+
const projects = projectsData.map(proj => { return proj.ProjectName }).slice(0, 10);
38+
const assignees = tasksData.map(task => { return task.assignedTo });
39+
const statuses = tasksData.map(task => { return task.status });
40+
41+
const breadcrumbItems: DataModel[] = [
42+
{
43+
id: "home",
44+
icon: <SvgIcon icon={homeIcon} />,
45+
},
46+
{
47+
id: "tasks",
48+
text: "Tasks",
49+
},
50+
{
51+
id: `${params.taskId}`,
52+
text: `New Task`,
53+
}
54+
];
55+
56+
const handleItemSelect = (e: BreadcrumbLinkMouseEvent) => {
57+
if (e.id === 'home') {
58+
navigate('/');
59+
} else if (e.id === 'tasks') {
60+
navigate('/tasks');
61+
}
62+
}
63+
64+
const tagRender = (tagData: TagData, li: ReactElement<any>) => React.cloneElement(li, li.props, [
65+
<span key={assignees.indexOf(tagData.data[0])} className="k-chip-label">
66+
<Avatar rounded="full" type="image" size="small" className="k-chip-avatar mr-1">
67+
<img src={projectManagers.map(manager => manager.name === tagData.data[0] ? manager.avatarSrc : "").filter(src => src !== "")[0]} alt="user-image" />
68+
</Avatar>
69+
{tagData.data[0]}
70+
</span>, li.props.children]);
71+
72+
const itemRender = (li: React.ReactElement<HTMLLIElement>, itemProps: ListItemProps) => {
73+
const index = itemProps.index;
74+
console.log(itemProps);
75+
const itemChildren = (
76+
<span key={index}>
77+
<Avatar rounded="full" type="image" size="small" className="mr-1">
78+
<img src={projectManagers.map(manager => manager.name === itemProps.dataItem ? manager.avatarSrc : "").filter(src => src !== "")[0]} alt="user-image" />
79+
</Avatar>
80+
{li.props.children as any} {index}
81+
</span>
82+
);
83+
84+
return React.cloneElement(li, li.props, itemChildren);
85+
};
86+
87+
const priorityValueRender = (element: React.ReactElement<HTMLSpanElement>, value: any) => {
88+
if (!value) {
89+
return element;
90+
}
91+
92+
const children = [
93+
<Badge
94+
key={value}
95+
rounded="full"
96+
position="inside"
97+
className="!relative !z-0"
98+
themeColor={
99+
value === "Urgent"
100+
? "error"
101+
: value === "Medium priority"
102+
? "warning"
103+
: value === "Low priority"
104+
? "success"
105+
: value === "Routine"
106+
? "tertiary"
107+
: "primary"
108+
}
109+
>
110+
{element.props.children as any}
111+
</Badge>
112+
];
113+
114+
return React.cloneElement(element, { ...element.props }, children);
115+
};
116+
117+
const priorityItemRender = (li: React.ReactElement<HTMLLIElement>, itemProps: ListItemProps) => {
118+
const itemChildren = (
119+
<Badge
120+
rounded="full"
121+
position="inside"
122+
className="!relative !z-0"
123+
themeColor={
124+
itemProps.dataItem === "Urgent"
125+
? "error"
126+
: itemProps.dataItem === "Medium priority"
127+
? "warning"
128+
: itemProps.dataItem === "Low priority"
129+
? "success"
130+
: itemProps.dataItem === "Routine"
131+
? "tertiary"
132+
: "primary"
133+
}
134+
>
135+
{li.props.children as any}
136+
</Badge>
137+
);
138+
139+
return React.cloneElement(li, li.props, itemChildren);
140+
};
141+
142+
return (
143+
<>
144+
<div style={{minHeight: 'calc(100vh - 106px)'}} className="flex flex-col p-10 gap-6">
145+
<Breadcrumb data={breadcrumbItems} onItemSelect={handleItemSelect} className="!bg-app-surface" />
146+
147+
<h1 className="text-4xl">New Task</h1>
148+
149+
<div className="grid grid-cols-12 gap-6">
150+
<div className="col-span-6 lg:col-span-8">
151+
<TextArea rows={30} className="rounded-t-2xl"/>
152+
<div className="bg-surface-alt border-1 border-t-0 border-border rounded-b-2xl px-4 py-2">
153+
<div className="hidden lg:flex gap-1">
154+
<Button svgIcon={checkIcon} themeColor="primary" size="large">Save changes</Button>
155+
<Button svgIcon={folderIcon} fillMode="flat" size="large" className="ml-auto">Archive Task</Button>
156+
<Button svgIcon={trashIcon} fillMode="flat" themeColor="error" size="large">Delete task</Button>
157+
</div>
158+
<div className="flex lg:hidden gap-1">
159+
<Button svgIcon={checkIcon} themeColor="primary" size="large">Save changes</Button>
160+
<Button svgIcon={folderIcon} fillMode="flat" size="large" className="ml-auto">Archive Task</Button>
161+
<Button svgIcon={trashIcon} fillMode="flat" themeColor="error" size="large">Delete task</Button>
162+
</div>
163+
</div>
164+
</div>
165+
166+
<div className="col-span-6 lg:col-span-4 flex flex-col gap-2">
167+
<ExpansionPanel title="Project" expanded={projExpanded} onAction={() => setProjExpanded(!projExpanded)} className="rounded-2xl">
168+
<Reveal>
169+
{projExpanded && <ExpansionPanelContent>
170+
<FloatingLabel label="Choose project" editorId={'project'} editorValue={project} className="flex">
171+
<DropDownList size="large" value={project} onChange={e => setProject(e.value as string)} data={projects} />
172+
</FloatingLabel>
173+
</ExpansionPanelContent>}
174+
</Reveal>
175+
</ExpansionPanel>
176+
<ExpansionPanel title="Assigned to" expanded={assigneeExpanded} onAction={() => setAssigneeExpanded(!assigneeExpanded)} className="rounded-2xl">
177+
<Reveal>
178+
{assigneeExpanded && <ExpansionPanelContent>
179+
<FloatingLabel label="Select assignee(s)" editorId={'assignee'} editorValue={assigneeExpanded} className="flex">
180+
<MultiSelect size="large" data={assignees} value={assignee} onChange={e => setAssignee([...e.value] as string[])} tagRender={tagRender} itemRender={itemRender} />
181+
</FloatingLabel>
182+
</ExpansionPanelContent>}
183+
</Reveal>
184+
</ExpansionPanel>
185+
<ExpansionPanel title="Due Date" expanded={dateExpanded} onAction={() => setDateExpanded(!dateExpanded)} className="rounded-2xl">
186+
<Reveal>
187+
{dateExpanded && <ExpansionPanelContent>
188+
<FloatingLabel label="Set due date" editorId={'due-date'} editorValue={dateExpanded} className="flex">
189+
<DateInput size="large" value={dueDate} onChange={e => setDueDate(e.value as Date)} />
190+
</FloatingLabel>
191+
</ExpansionPanelContent>}
192+
</Reveal>
193+
</ExpansionPanel>
194+
<ExpansionPanel title="Priority" expanded={priorityExpanded} onAction={() => setPriorityExpanded(!priorityExpanded)} className="rounded-2xl">
195+
<Reveal>
196+
{priorityExpanded && <ExpansionPanelContent>
197+
<FloatingLabel label="Choose project" editorId={'priority'} editorValue={priority} className="flex">
198+
<DropDownList size="large" value={priority} onChange={e => setPriority(e.value as string)} data={priorities} valueRender={priorityValueRender} itemRender={priorityItemRender} />
199+
</FloatingLabel>
200+
</ExpansionPanelContent>}
201+
</Reveal>
202+
</ExpansionPanel>
203+
<ExpansionPanel title="Status" expanded={statusExpanded} onAction={() => setStatusExpanded(!statusExpanded)} className="rounded-2xl">
204+
<Reveal>
205+
{statusExpanded && <ExpansionPanelContent>
206+
<FloatingLabel label="Select status" editorId={'status'} editorValue={statusExpanded} className="flex">
207+
<DropDownList size="large" data={statuses} value={status} onChange={e => setStatus(e.value as string)} />
208+
</FloatingLabel>
209+
</ExpansionPanelContent>}
210+
</Reveal>
211+
</ExpansionPanel>
212+
<ExpansionPanel title="Tags" expanded={tagsExpanded} onAction={() => setTagsExpanded(!tagsExpanded)} className="rounded-2xl">
213+
<Reveal>
214+
{tagsExpanded && <ExpansionPanelContent>
215+
<FloatingLabel label="Select tags" editorId={'tags'} editorValue={tagsExpanded} className="flex">
216+
<MultiSelect size="large" data={taskTags} value={tag} onChange={e => setTag([...e.value] as string[])} />
217+
</FloatingLabel>
218+
</ExpansionPanelContent>}
219+
</Reveal>
220+
</ExpansionPanel>
221+
</div>
222+
</div>
223+
224+
</div>
225+
<div className="bg-surface-alt color-subtle p-2 text-center">
226+
<span>Copyright &#169; 2025 Progress Software. All rights reserved.</span>
227+
</div>
228+
</>
229+
)
230+
}

examples/kendo-react-freemium/src/pages/Settings.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,23 +129,23 @@ export default function Settings() {
129129
</div>
130130
<span className="text-lg text-subtle font-bold">Contacts</span>
131131
<div className="grid grid-cols-2 gap-6">
132-
<FloatingLabel label="Country" editorId={'country'} editorValue={countryValue}>
132+
<FloatingLabel label="Country" editorId={'country'} editorValue={countryValue} optional={true}>
133133
<TextBox size="large" value={countryValue} onChange={e => setCountryValue(e.value as string)} suffix={() => {
134134
return (
135135
<InputSuffix>
136136
<SvgIcon icon={xIcon} onClick={() => setCountryValue('')} />
137137
</InputSuffix>)
138138
}} />
139139
</FloatingLabel>
140-
<FloatingLabel label="City" editorId={'city'} editorValue={cityValue}>
140+
<FloatingLabel label="City" editorId={'city'} editorValue={cityValue} optional={true}>
141141
<TextBox size="large" value={cityValue} onChange={e => setCityValue(e.value as string)} suffix={() => {
142142
return (
143143
<InputSuffix>
144144
<SvgIcon icon={xIcon} onClick={() => setCityValue('')} />
145145
</InputSuffix>)
146146
}}/>
147147
</FloatingLabel>
148-
<FloatingLabel label="Address" editorId={'address'} editorValue={addressValue}>
148+
<FloatingLabel label="Address" editorId={'address'} editorValue={addressValue} optional={true}>
149149
<TextBox size="large" value={addressValue} onChange={e => setAddresslValue(e.value as string)} suffix={() => {
150150
return (
151151
<InputSuffix>

0 commit comments

Comments
 (0)