Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Web] Refactor/sidebar #3361

Merged
merged 4 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"words": [
" X",
" X ",
"hookform",
"accepte",
"Accordian",
"adipiscing",
Expand All @@ -24,6 +25,7 @@
"apidemodt",
"apidemodts",
"apidev",
"apikey",
"apisauce",
"apistage",
"apistagecivo",
Expand Down Expand Up @@ -92,6 +94,7 @@
"creatoe",
"dailyplan",
"Darkmode",
"DATACENTER",
"datas",
"dataToDisplay",
"daygrid",
Expand Down Expand Up @@ -240,6 +243,7 @@
"longpress",
"Lorem",
"lucide",
"mailchimp",
"mainconfig",
"mappagination",
"mathieudutour",
Expand Down Expand Up @@ -355,7 +359,6 @@
"tailess",
"Tailess",
"tailwindcss",
"timesheet-viewMode",
"tanstack",
"taskid",
"taskstatus",
Expand All @@ -367,6 +370,7 @@
"testid",
"timegrid",
"Timesheet",
"timesheet-viewMode",
"Timesheets",
"Timeslot",
"tinvitations",
Expand Down Expand Up @@ -400,6 +404,7 @@
"VERSONS",
"vertificalline",
"vhidden",
"Waitlist",
"WARNING️",
"wasabisys",
"webm",
Expand Down
4 changes: 4 additions & 0 deletions apps/web/.env
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,7 @@ NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
# Warning: IF TRUE This allows production builds to successfully complete even if
# your project has ESLint errors.
NEXT_IGNORE_ESLINT_ERROR_ON_BUILD=true

# Mailchimp
MAILCHIMP_API_KEY=
MAILCHIMP_LIST_ID=
Comment on lines +141 to +143
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Documentation needed for Mailchimp environment variables and security requirements

The codebase already implements validation checks for Mailchimp credentials in the subscription API route (apps/web/app/api/subscribe/route.ts), but lacks documentation about the environment variables setup and security requirements. Specific points that need documentation:

  • Required Mailchimp API key format (<key>-us3)
  • List ID requirements
  • Error handling for missing credentials
  • API permission scopes needed for the subscription functionality
  • Security best practices for storing these credentials
🔗 Analysis chain

Consider documenting security requirements for Mailchimp credentials.

Since these variables will contain sensitive API credentials:

  1. Ensure these values are properly set in deployment environments
  2. Consider adding validation checks in the subscription API route
  3. Document the required Mailchimp permission scopes in README or deployment docs

Let's check if there's any documentation about environment variables setup:


Let's search for any existing documentation about environment variables and Mailchimp configuration specifically:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for documentation about environment variables setup
fd -t f "README.md|DEPLOYMENT.md|CONTRIBUTING.md" | xargs rg -i "environment|env" -A 5

Length of output: 2477


Script:

#!/bin/bash
# Search for any documentation about environment variables setup in .env.sample or similar files
fd -t f ".env.sample|.env.example" | xargs cat

# Search for Mailchimp-related code and documentation
rg -i "mailchimp" -A 5

Length of output: 2070

4 changes: 4 additions & 0 deletions apps/web/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,7 @@ MEET_JWT_APP_SECRET=
# Warning: IF TRUE This allows production builds to successfully complete even if
# your project has ESLint errors.
NEXT_IGNORE_ESLINT_ERROR_ON_BUILD=true

# Mailchimp
MAILCHIMP_API_KEY=
MAILCHIMP_LIST_ID=
2 changes: 1 addition & 1 deletion apps/web/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-mixed-spaces-and-tabs */
'use client';
import 'react-loading-skeleton/dist/skeleton.css';
import '../../styles/globals.css';
import '@/styles/globals.css';

import clsx from 'clsx';
import { Provider } from 'jotai';
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/[locale]/page-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ function MainPage() {
showTimer={headerSize <= 11.8 && isTrackingEnabled}
className="h-full"
mainHeaderSlot={
<div className="sticky z-40 bg-white dark:bg-dark-high">
<div className="bg-white dark:bg-dark-high">
<div className={clsxm('bg-white dark:bg-dark-high ', !fullWidth && 'x-container')}>
<div className="mx-8-container mt-7 !px-0 flex flex-row items-start justify-between ">
<div className="mx-8-container my-3 !px-0 flex flex-row items-start justify-between ">
<div className="flex items-center justify-center h-10 gap-8">
<PeoplesIcon className="text-dark dark:text-[#6b7280] h-6 w-6" />

Expand Down
67 changes: 67 additions & 0 deletions apps/web/app/api/subscribe/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { NextRequest, NextResponse } from 'next/server';

export const POST = async (req: NextRequest) => {
// 1. Destructure the email address from the request body.
const reqData = (await req.json()) as {
email_address: string;
tags: string[];
captcha?: string;
};

if (!reqData.email_address) {
// 2. Throw an error if an email wasn't provided.
return NextResponse.json({ error: 'Email is required' }, { status: 400 });
}

if (!reqData.captcha) {
// 2. Display an error if the captcha code wasn't provided.
console.error('ERROR: Please provide required fields', 'STATUS: 400');
}

Comment on lines +3 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Strengthen input validation and security measures

The current input validation has several security concerns:

  1. Email validation is too permissive
  2. Captcha validation is ineffective (logs but doesn't block)
  3. Missing rate limiting could enable API abuse

Consider implementing these improvements:

 export const POST = async (req: NextRequest) => {
+  // Rate limiting
+  const ip = req.headers.get('x-forwarded-for') || 'unknown';
+  const rateLimiter = await getRateLimiter(ip);
+  if (!rateLimiter.isAllowed) {
+    return NextResponse.json(
+      { error: 'Too many requests' },
+      { status: 429 }
+    );
+  }
+
   const reqData = (await req.json()) as {
     email_address: string;
     tags: string[];
     captcha?: string;
   };
 
-  if (!reqData.email_address) {
+  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+  if (!reqData.email_address || !emailRegex.test(reqData.email_address)) {
     return NextResponse.json({ error: 'Email is required' }, { status: 400 });
   }
 
   if (!reqData.captcha) {
-    console.error('ERROR: Please provide required fields', 'STATUS: 400');
+    return NextResponse.json(
+      { error: 'Captcha verification failed' },
+      { status: 400 }
+    );
   }
+
+  // Verify captcha
+  const isCaptchaValid = await verifyCaptcha(reqData.captcha);
+  if (!isCaptchaValid) {
+    return NextResponse.json(
+      { error: 'Invalid captcha' },
+      { status: 400 }
+    );
+  }

Committable suggestion skipped: line range outside the PR's diff.

try {
// 3. Fetch the environment variables.
const LIST_ID = process.env.MAILCHIMP_LIST_ID;
const API_KEY = process.env.MAILCHIMP_API_KEY ? process.env.MAILCHIMP_API_KEY : '';
if (!LIST_ID || !API_KEY) {
throw new Error('Missing Mailchimp environment variables');
}
// 4. API keys are in the form <key>-us3.
const DATACENTER = API_KEY.split('-')[1];
Comment on lines +21 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve API key validation and error handling

The current API key handling is fragile and could fail silently with malformed keys.

Apply this improvement:

   try {
     const LIST_ID = process.env.MAILCHIMP_LIST_ID;
-    const API_KEY = process.env.MAILCHIMP_API_KEY ? process.env.MAILCHIMP_API_KEY : '';
+    const API_KEY = process.env.MAILCHIMP_API_KEY?.trim() || '';
+    
     if (!LIST_ID || !API_KEY) {
       throw new Error('Missing Mailchimp environment variables');
     }
-    const DATACENTER = API_KEY.split('-')[1];
+    
+    // Validate API key format
+    const API_KEY_REGEX = /^[a-f0-9]{32}-([a-z]{2}\d{1,2})$/;
+    const match = API_KEY.match(API_KEY_REGEX);
+    if (!match) {
+      throw new Error('Invalid Mailchimp API key format');
+    }
+    const DATACENTER = match[1];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
// 3. Fetch the environment variables.
const LIST_ID = process.env.MAILCHIMP_LIST_ID;
const API_KEY = process.env.MAILCHIMP_API_KEY ? process.env.MAILCHIMP_API_KEY : '';
if (!LIST_ID || !API_KEY) {
throw new Error('Missing Mailchimp environment variables');
}
// 4. API keys are in the form <key>-us3.
const DATACENTER = API_KEY.split('-')[1];
try {
// 3. Fetch the environment variables.
const LIST_ID = process.env.MAILCHIMP_LIST_ID;
const API_KEY = process.env.MAILCHIMP_API_KEY?.trim() || '';
if (!LIST_ID || !API_KEY) {
throw new Error('Missing Mailchimp environment variables');
}
// Validate API key format
const API_KEY_REGEX = /^[a-f0-9]{32}-([a-z]{2}\d{1,2})$/;
const match = API_KEY.match(API_KEY_REGEX);
if (!match) {
throw new Error('Invalid Mailchimp API key format');
}
const DATACENTER = match[1];

const mailchimpData = {
email_address: reqData.email_address,
status: 'subscribed',
tags: reqData.tags ? [...reqData.tags] : ['Ever Teams']
};
Comment on lines +30 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance data preparation and configuration flexibility

The current implementation has hardcoded values and lacks validation for tags.

Consider these improvements:

+    const DEFAULT_TAG = process.env.MAILCHIMP_DEFAULT_TAG || 'Ever Teams';
+    
+    // Validate tags
+    const validateTags = (tags: string[]) => {
+      return tags.every(tag => 
+        typeof tag === 'string' && 
+        tag.length <= 100 &&
+        /^[\w\s-]+$/.test(tag)
+      );
+    };
+
     const mailchimpData = {
       email_address: reqData.email_address,
       status: 'subscribed',
-      tags: reqData.tags ? [...reqData.tags] : ['Ever Teams']
+      tags: reqData.tags && validateTags(reqData.tags) 
+        ? [...reqData.tags] 
+        : [DEFAULT_TAG]
     };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const mailchimpData = {
email_address: reqData.email_address,
status: 'subscribed',
tags: reqData.tags ? [...reqData.tags] : ['Ever Teams']
};
const DEFAULT_TAG = process.env.MAILCHIMP_DEFAULT_TAG || 'Ever Teams';
// Validate tags
const validateTags = (tags: string[]) => {
return tags.every(tag =>
typeof tag === 'string' &&
tag.length <= 100 &&
/^[\w\s-]+$/.test(tag)
);
};
const mailchimpData = {
email_address: reqData.email_address,
status: 'subscribed',
tags: reqData.tags && validateTags(reqData.tags)
? [...reqData.tags]
: [DEFAULT_TAG]
};

// 5. Send a POST request to Mailchimp.
const response = await fetch(`https://${DATACENTER}.api.mailchimp.com/3.0/lists/${LIST_ID}/members`, {
body: JSON.stringify(mailchimpData),
headers: {
Authorization: `apikey ${API_KEY}`,
'Content-Type': 'application/json'
},
method: 'POST'
});
console.log(response);
// 6. Swallow any errors from Mailchimp and return a better error message.
if (response.status >= 400) {
const errorResponse = await response.json();
return NextResponse.json(
{
error: `There was an error subscribing to the newsletter: ${errorResponse.detail}`
},
{ status: 400 }
);
}

// 7. If we made it this far, it was a success! 🎉
return NextResponse.json({ error: '', resp: response }, { status: 201 });
} catch (error) {
return NextResponse.json(
{
error: (error as Error).message || (error as Error).toString(),
resp: null
},
{ status: 500 }
);
}
};
Comment on lines +1 to +67
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Based on the analysis of the configuration files and middleware, I can now provide a final response regarding the security concerns.

Security configurations need enhancement

The subscribe API endpoint requires additional security measures:

  • No CORS configuration is present in next.config.js
  • Missing Content-Security-Policy headers
  • No rate limiting implemented for the subscription endpoint
  • The captcha validation is logged but not enforced (code continues execution even when captcha is missing)

Recommended actions:

  • Add CORS configuration in next.config.js with specific allowed origins
  • Implement rate limiting for the subscription endpoint to prevent abuse
  • Add security headers including CSP
  • Enforce captcha validation by returning 400 status when captcha is missing
🔗 Analysis chain

Verify security configurations

Please ensure proper security headers and CORS configurations are in place.

Run the following script to check security configurations:


Let me gather more information about the security configurations by checking the Next.js configuration files and middleware implementations more thoroughly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for security configurations in the Next.js app

# Check for CORS configuration
echo "Checking CORS configuration..."
rg -A 5 "cors" apps/web/next.config.js
rg -A 5 "cors" apps/web/middleware.ts

# Check for security headers
echo "Checking security headers..."
rg -A 10 "headers" apps/web/next.config.js
rg -A 10 "headers" apps/web/middleware.ts

# Check for API route protection
echo "Checking API route protection..."
rg -A 5 "api" apps/web/middleware.ts

Length of output: 845


Script:

#!/bin/bash
# Check Next.js config and middleware for security configurations
echo "Checking Next.js config content..."
cat apps/web/next.config.js

echo -e "\nChecking middleware content..."
cat apps/web/middleware.ts

echo -e "\nChecking for CSP headers..."
rg -l "Content-Security-Policy" apps/web/

echo -e "\nChecking for rate limiting..."
rg -l "rate-limit" apps/web/

Length of output: 10357

Comment on lines +35 to +67
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve API handling and response management

Current implementation has several issues:

  1. Debug logging in production code
  2. Generic error handling
  3. Potentially sensitive data in response

Apply these improvements:

     const response = await fetch(`https://${DATACENTER}.api.mailchimp.com/3.0/lists/${LIST_ID}/members`, {
       body: JSON.stringify(mailchimpData),
       headers: {
         Authorization: `apikey ${API_KEY}`,
         'Content-Type': 'application/json'
       },
       method: 'POST'
     });
-    console.log(response);
 
     if (response.status >= 400) {
       const errorResponse = await response.json();
+      const errorMessage = errorResponse.title || errorResponse.detail || 'Subscription failed';
       return NextResponse.json(
         {
-          error: `There was an error subscribing to the newsletter: ${errorResponse.detail}`
+          error: errorMessage
         },
         { status: response.status }
       );
     }
 
-    return NextResponse.json({ error: '', resp: response }, { status: 201 });
+    return NextResponse.json(
+      { 
+        message: 'Successfully subscribed to newsletter',
+        status: 'success'
+      }, 
+      { status: 201 }
+    );
   } catch (error) {
+    console.error('Newsletter subscription error:', error);
     return NextResponse.json(
       {
-        error: (error as Error).message || (error as Error).toString(),
-        resp: null
+        error: 'An unexpected error occurred'
       },
       { status: 500 }
     );
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 5. Send a POST request to Mailchimp.
const response = await fetch(`https://${DATACENTER}.api.mailchimp.com/3.0/lists/${LIST_ID}/members`, {
body: JSON.stringify(mailchimpData),
headers: {
Authorization: `apikey ${API_KEY}`,
'Content-Type': 'application/json'
},
method: 'POST'
});
console.log(response);
// 6. Swallow any errors from Mailchimp and return a better error message.
if (response.status >= 400) {
const errorResponse = await response.json();
return NextResponse.json(
{
error: `There was an error subscribing to the newsletter: ${errorResponse.detail}`
},
{ status: 400 }
);
}
// 7. If we made it this far, it was a success! 🎉
return NextResponse.json({ error: '', resp: response }, { status: 201 });
} catch (error) {
return NextResponse.json(
{
error: (error as Error).message || (error as Error).toString(),
resp: null
},
{ status: 500 }
);
}
};
// 5. Send a POST request to Mailchimp.
const response = await fetch(`https://${DATACENTER}.api.mailchimp.com/3.0/lists/${LIST_ID}/members`, {
body: JSON.stringify(mailchimpData),
headers: {
Authorization: `apikey ${API_KEY}`,
'Content-Type': 'application/json'
},
method: 'POST'
});
// 6. Swallow any errors from Mailchimp and return a better error message.
if (response.status >= 400) {
const errorResponse = await response.json();
const errorMessage = errorResponse.title || errorResponse.detail || 'Subscription failed';
return NextResponse.json(
{
error: errorMessage
},
{ status: response.status }
);
}
// 7. If we made it this far, it was a success! 🎉
return NextResponse.json(
{
message: 'Successfully subscribed to newsletter',
status: 'success'
},
{ status: 201 }
);
} catch (error) {
console.error('Newsletter subscription error:', error);
return NextResponse.json(
{
error: 'An unexpected error occurred'
},
{ status: 500 }
);
}
};

132 changes: 69 additions & 63 deletions apps/web/components/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,36 @@ import {
MonitorSmartphone,
LayoutDashboard,
Heart,
FolderKanban,
SquareActivity,
PlusIcon,
Files,
X
X,
Command,
AudioWaveform,
GalleryVerticalEnd
} from 'lucide-react';

import { EverTeamsLogo, SymbolAppLogo } from '@/lib/components/svgs';
import { NavMain } from '@/components/nav-main';
import {
Sidebar,
SidebarContent,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
SidebarTrigger,
useSidebar,
SidebarMenuSubButton
SidebarMenuSubButton,
SidebarFooter
} from '@/components/ui/sidebar';
import Link from 'next/link';
import { cn } from '@/lib/utils';
import { useOrganizationAndTeamManagers } from '@/app/hooks/features/useOrganizationTeamManagers';
import { useAuthenticateUser, useModal, useOrganizationTeams } from '@/app/hooks';
import { useFavoritesTask } from '@/app/hooks/features/useFavoritesTask';
import { Button } from '@/lib/components/button';
import { CreateTeamModal, TaskIssueStatus } from '@/lib/features';
import { useTranslations } from 'next-intl';
import { WorkspacesSwitcher } from './workspace-switcher';
import { SidebarOptInForm } from './sidebar-opt-in-form';
import { NavProjects } from './nav-projects';
type AppSidebarProps = React.ComponentProps<typeof Sidebar> & { publicTeam: boolean | undefined };
export function AppSidebar({ publicTeam, ...props }: AppSidebarProps) {
const { userManagedTeams } = useOrganizationAndTeamManagers();
const { user } = useAuthenticateUser();
const username = user?.name || user?.firstName || user?.lastName || user?.username;
const { isTeamManager } = useOrganizationTeams();
Expand All @@ -44,11 +42,57 @@ export function AppSidebar({ publicTeam, ...props }: AppSidebarProps) {
const t = useTranslations();
// This is sample data.
const data = {
user: {
name: 'evereq',
email: 'evereq@ever.co',
avatar: '/assets/svg/profile.svg'
},
workspaces: [
{
name: 'Ever Teams',
logo: ({ className }: { className?: string }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={25}
height={26}
viewBox="0 0 25 26"
fill="none"
className={cn('size-5', className)}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M.55 18.186l.325-.773 2.04-4.855-.007-.012-1.555-3.127-.167 6.846-.437-.01.173-7.14.283.006-.463-.93-.376-.756.814-.222 4.758-1.298L8.187.935l.348-.773.688.494.805.579.055-.27 7.701 1.565-.057.283 1.34-.367.915-.25-.039.947-.049 1.188.262-.13 3.286 6.604-.392.195-3.168-6.366-.164 4.005 4.177 3.116.701.524-.67.563-4.023 3.38v.003l-.018.015-.123 4.096 3.26-6.716.395.191-3.43 7.063-.238-.116-.03.997-.024.822-.806-.163-5.184-1.047-3.92 3.117-.67.533-.383-.767-.497-.999-.249.317-5.856-4.61.27-.344L8.2 23.177 6.324 19.41 1.37 18.36l-.82-.173zM13.743 3.905L10.35 1.466 17.408 2.9l-3.666 1.005zM2.479 17.177l1.25-2.98 1.806 3.627-3.056-.647zm4.788 1.015l.018.036 6.066 1.617 5.147-4.258-.17-6.256-.025-.018.002-.051-4.86-3.844-6.516 1.67-2.484 5.433 2.821 5.67zm2.325 4.673l-1.484-2.982 3.92 1.045-2.436 1.937zm8.766-1.973l-3.293-.665 3.397-2.81-.104 3.475zm4.005-8.549l-2.508 2.108-.111-4.063 2.62 1.955zM18.52 4.034l-.144 3.515-3.264-2.581 3.408-.934zM9.102 2.277l2.894 2.08-4.335 1.111 1.441-3.19zM2.359 8.33l2.83-.773-1.539 3.367L2.36 8.33zm-.087-1.78l5.134-4.742-.297-.322-5.134 4.742.297.322zm15.641 16.259l-6.936 1.61-.099-.426 6.936-1.61.1.426z"
fill="url(#paint0_linear_11058_107682)"
/>
<defs>
<linearGradient
id="paint0_linear_11058_107682"
x1="-2.65811"
y1="11.7373"
x2="11.928"
y2="4.38343"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#D24F39" />
<stop offset={1} stopColor="#791EEC" />
</linearGradient>
</defs>
</svg>
),
plan: 'Enterprise'
},
{
name: 'Ever Gauzy',
logo: AudioWaveform,
plan: 'Startup'
},
{
name: 'Ever Cloc',
logo: GalleryVerticalEnd,
plan: 'Free'
},
{
name: 'Ever Rec',
logo: Command,
plan: 'Free'
}
],
navMain: [
{
title: t('sidebar.DASHBOARD'),
Expand Down Expand Up @@ -138,35 +182,6 @@ export function AppSidebar({ publicTeam, ...props }: AppSidebarProps) {
}
]
},
...(userManagedTeams && userManagedTeams.length > 0
? [
{
title: t('sidebar.PROJECTS'),
label: 'projects',
url: '#',
icon: FolderKanban,
items: [
{
title: t('common.NO_PROJECT'),
label: 'no-project',
url: '#',
component: (
<SidebarMenuSubButton asChild>
<Button
className="w-full text-xs mt-3 dark:text-white rounded-xl border-[0.0938rem]"
variant="outline"
disabled={!user?.isEmailVerified}
>
<PlusIcon className="w-4 h-4" />
{t('common.CREATE_PROJECT')}
</Button>
</SidebarMenuSubButton>
)
}
]
}
]
: []),
{
title: t('sidebar.MY_WORKS'),
url: '#',
Expand Down Expand Up @@ -232,7 +247,8 @@ export function AppSidebar({ publicTeam, ...props }: AppSidebarProps) {
}
]
: [])
]
],
projects: []
};

return (
Expand All @@ -245,30 +261,20 @@ export function AppSidebar({ publicTeam, ...props }: AppSidebarProps) {
<SidebarTrigger
className={cn(
state === 'collapsed' ? 'right-[-20%]' : ' right-[-5%]',
'absolute top-[10.5%] size-7 !bg-[#1C75FD] flex items-center justify-center !rounded-full transition-all duration-300 filter drop-shadow-[0px_0px_6px_rgba(28,117,253,0.30)] z-[55]'
'absolute top-[8%] size-7 !bg-[#1C75FD] flex items-center justify-center !rounded-full transition-all duration-300 filter drop-shadow-[0px_0px_6px_rgba(28,117,253,0.30)] z-[55]'
)}
/>
<SidebarHeader className={cn('mb-[1.4rem]', state === 'collapsed' ? 'items-center' : '')}>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
className={cn(state === 'collapsed' ? 'items-center justify-center' : '')}
size="lg"
asChild
>
<Link href="/">
<div className="flex items-center justify-center rounded-lg aspect-square size-8 text-sidebar-primary-foreground">
<SymbolAppLogo className="size-5" />
</div>
{state === 'expanded' && <EverTeamsLogo dash />}
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
<WorkspacesSwitcher workspaces={data.workspaces} />
</SidebarHeader>
<SidebarContent>
<NavMain items={data.navMain} />
<NavProjects projects={data.projects} />
</SidebarContent>

<SidebarFooter className="p-1 mt-auto">
<SidebarOptInForm />
</SidebarFooter>
<SidebarRail />
</Sidebar>

Expand Down
Loading
Loading