UI changes for team dashboard

feat/team-dashboard
Kamran Ahmed 2 weeks ago
parent 56a038363e
commit 0e787b49c6
  1. 2
      .astro/settings.json
  2. 8
      src/components/Activity/ActivityStream.tsx
  3. 2
      src/components/Activity/EmptyActivity.tsx
  4. 21
      src/components/Activity/EmptyStream.tsx
  5. 67
      src/components/Dashboard/DashboardTeamRoadmaps.tsx
  6. 17
      src/components/Dashboard/TeamDashboard.tsx
  7. 2
      src/components/TeamActivity/TeamActivityItem.tsx
  8. 16
      src/components/TeamActivity/TeamActivityPage.tsx
  9. 2
      src/components/TeamActivity/TeamEmptyStream.tsx
  10. 11
      src/components/TeamMemberDetails/TeamMemberEmptyPage.tsx
  11. 6
      src/components/TeamRoadmapsList/TeamRoadmaps.tsx
  12. 48
      src/data/projects/log-analyser.md

@ -3,6 +3,6 @@
"enabled": false
},
"_variables": {
"lastUpdateCheck": 1727095669945
"lastUpdateCheck": 1728296475293
}
}

@ -56,9 +56,11 @@ export function ActivityStream(props: ActivityStreamProps) {
return (
<div className={cn('mx-0 px-0 py-5 md:-mx-10 md:px-8 md:py-8', className)}>
<h2 className="mb-3 text-xs uppercase text-gray-400">
Learning Activity
</h2>
{activities.length > 0 && (
<h2 className="mb-3 text-xs uppercase text-gray-400">
Learning Activity
</h2>
)}
{selectedActivity && (
<ActivityTopicsModal

@ -4,7 +4,7 @@ export function EmptyActivity() {
return (
<div className="rounded-md">
<div className="flex flex-col items-center p-7 text-center">
<RoadmapIcon className="mb-2 w-[60px] h-[60px] sm:h-[120px] sm:w-[120px] opacity-10" />
<RoadmapIcon className="mb-2 h-14 w-14 opacity-10" />
<h2 className="text-lg sm:text-xl font-bold">No Progress</h2>
<p className="my-1 sm:my-2 max-w-[400px] text-gray-500 text-sm sm:text-base">

@ -4,26 +4,11 @@ export function EmptyStream() {
return (
<div className="rounded-md">
<div className="flex flex-col items-center p-7 text-center">
<List className="mb-4 h-[60px] w-[60px] opacity-10 sm:h-[60px] sm:w-[60px]" />
<List className="mb-4 h-14 w-14 opacity-10" />
<h2 className="text-lg font-bold sm:text-xl">No Activities</h2>
<h2 className="text-lg font-bold sm:text-xl">No Activity</h2>
<p className="my-1 max-w-[400px] text-balance text-sm text-gray-500 sm:my-2 sm:text-base">
Activities will appear here as you start tracking your&nbsp;
<a href="/roadmaps" className="mt-4 text-blue-500 hover:underline">
Roadmaps
</a>
,&nbsp;
<a
href="/best-practices"
className="mt-4 text-blue-500 hover:underline"
>
Best Practices
</a>
&nbsp;or&nbsp;
<a href="/questions" className="mt-4 text-blue-500 hover:underline">
Questions
</a>
&nbsp;progress.
Activities will appear here as you start tracking your progress.
</p>
</div>
</div>

@ -202,32 +202,49 @@ export function DashboardTeamRoadmaps(props: DashboardTeamRoadmapsProps) {
/>
);
const roadmapHeading = (
<div className="mb-3 flex h-[20px] items-center justify-between gap-2 text-xs">
<h2 className="uppercase text-gray-400">Roadmaps</h2>
{canManageCurrentTeam && (
<a
href={`/team/roadmaps?t=${teamId}`}
className="rounded-full bg-gray-400 px-2.5 py-0.5 text-xs text-white transition-colors hover:bg-black"
>
Manage Roadmaps
</a>
)}
</div>
);
if (!isLoading && learningRoadmapsToShow.length === 0) {
return (
<div className="flex flex-col items-center p-4 py-10">
{pickRoadmapOptionModal}
{addRoadmapModal}
{createRoadmapModal}
{confirmationContentIdModal}
<RoadmapIcon className="mb-4 h-24 w-24 opacity-10" />
<h3 className="mb-1 text-2xl font-bold text-gray-900">No roadmaps</h3>
<p className="text-base text-gray-500">
{canManageCurrentTeam
? 'Add a roadmap to start tracking your team'
: 'Ask your team admin to add some roadmaps'}
</p>
{canManageCurrentTeam && (
<button
className="mt-4 rounded-lg bg-black px-4 py-2 font-medium text-white hover:bg-gray-900"
onClick={() => setIsPickingOptions(true)}
>
Add roadmap
</button>
)}
</div>
<>
{roadmapHeading}
<div className="flex flex-col items-center rounded-md border bg-white p-4 py-10">
{pickRoadmapOptionModal}
{addRoadmapModal}
{createRoadmapModal}
{confirmationContentIdModal}
<RoadmapIcon className="mb-4 h-14 w-14 opacity-10" />
<h2 className="text-lg font-semibold sm:text-lg">No roadmaps</h2>
<p className="my-1 max-w-[400px] text-balance text-sm text-gray-500 sm:my-2 sm:text-base">
{canManageCurrentTeam
? 'Add a roadmap to start tracking your team'
: 'Ask your team admin to add some roadmaps'}
</p>
{canManageCurrentTeam && (
<button
className="mt-1 rounded-lg bg-black px-3 py-1 text-sm font-medium text-white hover:bg-gray-900"
onClick={() => setIsPickingOptions(true)}
>
Add roadmap
</button>
)}
</div>
</>
);
}
@ -238,7 +255,7 @@ export function DashboardTeamRoadmaps(props: DashboardTeamRoadmapsProps) {
{createRoadmapModal}
{confirmationContentIdModal}
<h2 className="mb-3 text-xs uppercase text-gray-400">Roadmaps</h2>
{roadmapHeading}
{isLoading && <LoadingProgress />}
{!isLoading && learningRoadmapsToShow.length > 0 && (
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-3">

@ -3,13 +3,9 @@ import type { TeamMember } from '../TeamProgress/TeamProgressPage';
import { httpGet } from '../../lib/http';
import { useToast } from '../../hooks/use-toast';
import { getUser } from '../../lib/jwt';
import { LoadingProgress } from './LoadingProgress';
import { ResourceProgress } from '../Activity/ResourceProgress';
import { TeamActivityPage } from '../TeamActivity/TeamActivityPage';
import { cn } from '../../lib/classname';
import { Tooltip } from '../Tooltip';
import { DashboardCardLink } from './DashboardCardLink';
import { PencilRuler } from 'lucide-react';
import { DashboardTeamRoadmaps } from './DashboardTeamRoadmaps';
import type { BuiltInRoadmap } from './PersonalDashboard';
import { InviteMemberPopup } from '../TeamMembers/InviteMemberPopup';
@ -34,6 +30,7 @@ export function TeamDashboard(props: TeamDashboardProps) {
const { response, error } = await httpGet<TeamMember[]>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-team-progress/${teamId}`,
);
if (error || !response) {
toast.error(error?.message || 'Failed to get team progress');
return;
@ -111,8 +108,16 @@ export function TeamDashboard(props: TeamDashboardProps) {
builtInSkillRoadmaps={builtInSkillRoadmaps}
/>
<h2 className="mb-3 mt-6 text-xs uppercase text-gray-400">
<h2 className="mb-3 mt-6 flex h-[20px] items-center justify-between text-xs uppercase text-gray-400">
Team Members
{canManageCurrentTeam && (
<a
href={`/team/members?t=${teamId}`}
className="rounded-full bg-gray-400 px-2.5 py-0.5 text-xs text-white transition-colors hover:bg-black"
>
Manage Members
</a>
)}
</h2>
{isLoading && <TeamMemberLoading className="mb-6" />}
{!isLoading && (
@ -157,7 +162,7 @@ export function TeamDashboard(props: TeamDashboardProps) {
</div>
)}
{!isLoading && <TeamActivityPage teamId={teamId} />}
<TeamActivityPage teamId={teamId} />
</section>
);
}

@ -102,7 +102,7 @@ export function TeamActivityItem(props: TeamActivityItemProps) {
return (
<li
key={user._id}
className="flex flex-wrap items-center gap-1 rounded-md border px-2 py-2.5 text-sm"
className="flex flex-wrap items-center gap-1 rounded-md border px-2 py-2.5 text-sm bg-white"
>
{actionType === 'in_progress' && (
<>

@ -183,8 +183,11 @@ export function TeamActivityPage(props: TeamActivityPageProps) {
}, [users, activities]);
if (!teamId) {
window.location.href = '/';
return;
if (typeof window !== 'undefined') {
window.location.href = '/';
} else {
return null;
}
}
if (isLoading) {
@ -233,7 +236,14 @@ export function TeamActivityPage(props: TeamActivityPageProps) {
/>
</>
) : (
<TeamEmptyStream teamId={teamId} />
<>
<h3 className="mb-4 flex w-full items-center justify-between text-xs uppercase text-gray-400">
Team Activity
</h3>
<div className="rounded-lg border bg-white p-4">
<TeamEmptyStream teamId={teamId} />
</div>
</>
)}
</>
);

@ -10,7 +10,7 @@ export function TeamEmptyStream(props: TeamActivityItemProps) {
return (
<div className="rounded-md">
<div className="flex flex-col items-center p-7 text-center sm:p-14">
<ListTodo className="mb-4 h-[60px] w-[60px] opacity-10 sm:h-[60px] sm:w-[60px]" />
<ListTodo className="mb-4 h-14 w-14 opacity-10" />
<h2 className="text-lg font-semibold sm:text-lg">No Activity</h2>
<p className="my-1 max-w-[400px] text-balance text-sm text-gray-500 sm:my-2 sm:text-base">

@ -10,18 +10,11 @@ export function TeamMemberEmptyPage(props: TeamMemberEmptyPageProps) {
return (
<div className="rounded-md">
<div className="flex flex-col items-center p-7 text-center">
<RoadmapIcon className="mb-2 h-[60px] w-[60px] opacity-10 sm:h-[120px] sm:w-[120px]" />
<RoadmapIcon className="mb-2 h-14 w-14 opacity-10" />
<h2 className="text-lg font-bold sm:text-xl">No Progress</h2>
<p className="my-1 max-w-[400px] text-balance text-sm text-gray-500 sm:my-2 sm:text-base">
Progress will appear here as they start tracking their{' '}
<a
href={`/team/roadmaps?t=${teamId}`}
className="mt-4 text-blue-500 hover:underline"
>
Roadmaps
</a>{' '}
progress.
Progress will appear here as they start tracking their roadmaps.
</p>
</div>
</div>

@ -309,9 +309,9 @@ export function TeamRoadmaps() {
{createRoadmapModal}
{confirmationContentIdModal}
<RoadmapIcon className="mb-4 h-24 w-24 opacity-10" />
<RoadmapIcon className="mb-3 h-14 w-14 opacity-10" />
<h3 className="mb-1 text-2xl font-bold text-gray-900">No roadmaps</h3>
<h3 className="mb-1 text-xl font-bold text-gray-900">No roadmaps</h3>
<p className="text-base text-gray-500">
{canManageCurrentTeam
? 'Add a roadmap to start tracking your team'
@ -320,7 +320,7 @@ export function TeamRoadmaps() {
{canManageCurrentTeam && (
<button
className="mt-4 rounded-lg bg-black px-4 py-2 font-medium text-white hover:bg-gray-900"
className="mt-3 rounded-md bg-black px-3 py-1.5 font-medium text-white hover:bg-gray-900 text-sm"
onClick={() => setIsPickingOptions(true)}
>
Add roadmap

@ -0,0 +1,48 @@
---
title: 'Log Analysis Tool'
description: 'Write a simple tool to analyze logs from the command line.'
isNew: true
sort: 3
difficulty: 'beginner'
nature: 'CLI'
skills:
- 'linux'
- 'bash'
- 'shell scripting'
seo:
title: 'Log Analysis Tool'
description: 'Build a simple CLI tool to analyze logs from the command line.'
keywords:
- 'log analysis tool'
- 'devops project idea'
roadmapIds:
- 'devops'
- 'linux'
---
The goal of this project is to help you practice some basic shell scripting skills. You will write a simple tool to analyze logs from the command line.
## Requirements
Download the sample nginx access log file from [here](https://gist.githubusercontent.com/kamranahmedse/e66c3b9ea89a1a030d3b739eeeef22d0/raw/77fb3ac837a73c4f0206e78a236d885590b7ae35/nginx-access.log). The log file contains the following fields:
- IP address
- Date and time
- Request method and path
- Response status code
- Response size
- Referrer
- User agent
You are required to create a shell script that reads the log file and provides the following information:
```text
Top 5 IP addresses with the most requests:
45.76.135.253 - 1000 requests
142.93.143.8 - 600 requests
178.128.94.113 - 50 requests
43.224.43.187 - 30 requests
178.128.94.113 - 20 requests
```
Loading…
Cancel
Save