Skip to content

Commit

Permalink
Merge pull request #118 from codesquad-team4-issue-tracker/feat/#77-i…
Browse files Browse the repository at this point in the history
…ssues-crud

[FE] 이슈 및 코멘트 CRUD
  • Loading branch information
aaaz425 authored Aug 17, 2023
2 parents 1cb227b + 0260f2d commit e3612e0
Show file tree
Hide file tree
Showing 41 changed files with 1,073 additions and 706 deletions.
53 changes: 14 additions & 39 deletions fe/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { Routes, Route, useLocation } from 'react-router-dom';
import { useState } from 'react';
import { Theme, ThemeProvider, css } from '@emotion/react';
import { themes } from './styles/styles';
import Header from './components/Header/Header';
import SignIn from './components/Sign/SignIn';
import SignUp from './components/Sign/SignUp';
Expand All @@ -10,48 +7,26 @@ import IssueCreate from './components/Issue/IssueCreate/IssueCreate';
import IssueDetail from './components/Issue/IssueDetail/IssueDetail';
import LabelList from './components/Label/LabelList';
import MilestoneList from './components/Milestone/MilestoneList';
import ThemeToggle from './components/ThemeToggle';
import UserProvider from './components/Context/UserContext';

export default function App() {
const location = useLocation();
const hiddenHeaderRoutes = ['/sign-in', '/sign-up'];
const shouldShowHeader = !hiddenHeaderRoutes.includes(location.pathname);

const [darkMode, setDarkMode] = useState(false);
const theme = darkMode ? themes.dark : themes.light;

const onThemeToggleClick = () => {
setDarkMode((prevMode) => !prevMode);
};

return (
<ThemeProvider theme={theme}>
<div css={body(theme)}>
{shouldShowHeader && <Header />}
<Routes>
<Route path="/" element={<IssueList />} />
<Route path="/sign-in" element={<SignIn />} />
<Route path="/sign-up" element={<SignUp />} />
<Route path="/issue" element={<IssueList />} />
<Route path="/issue-create" element={<IssueCreate />} />
<Route path="/issue/:id" element={<IssueDetail />} />
<Route path="/label" element={<LabelList />} />
<Route path="/milestone" element={<MilestoneList />} />
</Routes>
<ThemeToggle onClick={onThemeToggleClick} isDark={darkMode} />
</div>
</ThemeProvider>
<UserProvider>
{shouldShowHeader && <Header />}
<Routes>
<Route path="/" element={<IssueList />} />
<Route path="/sign-in" element={<SignIn />} />
<Route path="/sign-up" element={<SignUp />} />
<Route path="/issue" element={<IssueList />} />
<Route path="/issue-create" element={<IssueCreate />} />
<Route path="/issue/:id" element={<IssueDetail />} />
<Route path="/label" element={<LabelList />} />
<Route path="/milestone" element={<MilestoneList />} />
</Routes>
</UserProvider>
);
}

const body = (theme: Theme) => css`
display: flex;
flex-direction: column;
height: 100vh;
background-color: ${theme.neutral.surfaceDefault};
& svg path {
stroke: ${theme.neutral.textDefault};
}
`;
Binary file added fe/src/assets/user/default.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 78 additions & 0 deletions fe/src/components/Context/UserContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { ReactNode, createContext, useEffect, useState } from 'react';
import { User } from '../../type/User.type';
import { Theme, ThemeProvider, css } from '@emotion/react';
import { themes } from '../../styles/styles';

type UserProviderProps = {
children: ReactNode;
};

export const UserContext = createContext<{
user?: User;
setUser?: React.Dispatch<React.SetStateAction<User>>;
darkMode?: boolean;
onToggleTheme?: () => void;
} | null>(null);

export default function UserProvider({ children }: UserProviderProps) {
const [user, setUser] = useState<User>(() => {
return {
accessToken: localStorage.getItem('accessToken') || '',
refreshToken: localStorage.getItem('refreshToken') || '',
member: {
id: Number(localStorage.getItem('id')) || 0,
name: localStorage.getItem('name') || '',
imageUrl: localStorage.getItem('imageUrl') || '',
},
};
});
const [darkMode, setDarkMode] = useState(() => {
const storedDarkMode = localStorage.getItem('darkMode');
return storedDarkMode ? JSON.parse(storedDarkMode) : false;
});

useEffect(() => {
setUser({
accessToken: localStorage.getItem('accessToken') || '',
refreshToken: localStorage.getItem('refreshToken') || '',
member: {
id: Number(localStorage.getItem('id')) || 0,
name: localStorage.getItem('name') || '',
imageUrl: localStorage.getItem('imageUrl') || '',
},
});
}, []);

useEffect(() => {
localStorage.setItem('darkMode', String(darkMode));
}, [darkMode]);

const onToggleTheme = () => {
setDarkMode((prevMode: boolean) => !prevMode);
};

const theme = darkMode ? themes.dark : themes.light;

return (
<UserContext.Provider value={{ user, setUser, darkMode, onToggleTheme }}>
<ThemeProvider theme={theme}>
<div css={body(theme)}>{children}</div>
</ThemeProvider>
</UserContext.Provider>
);
}

const body = (theme: Theme) => css`
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
flex-grow: 1;
overflow: auto;
background-color: ${theme.neutral.surfaceDefault};
& svg path {
stroke: ${theme.neutral.textDefault};
}
`;
21 changes: 10 additions & 11 deletions fe/src/components/FilterBar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Theme, css, useTheme } from '@emotion/react';
import { ReactComponent as ChevronDownIcon } from '../assets/icon/chevronDown.svg';
import { ReactComponent as SearchIcon } from '../assets/icon/search.svg';
import { border, font, radius } from '../styles/styles';

export default function FilterBar() {
Expand All @@ -11,12 +12,10 @@ export default function FilterBar() {
필터
<ChevronDownIcon />
</button>
<input
className="filter-input"
type="text"
value="is:issue is:open"
onChange={() => {}}
/>
<div className="filter-input">
<SearchIcon />
is:issue is:open
</div>
</div>
);
}
Expand All @@ -42,18 +41,18 @@ const filterBar = (theme: Theme) => css`
}
.filter-input {
position: relative;
display: flex;
align-items: center;
gap: 8px;
width: 440px;
height: 40px;
padding-left: 40px;
padding: 0 24px;
border: none;
border-left: ${border.default} ${theme.neutral.borderDefault};
border-radius: 0 ${radius.medium} ${radius.medium} 0;
color: ${theme.neutral.textWeak};
background-color: ${theme.neutral.surfaceBold};
font: ${font.displayMedium16};
background-image: url('../../src/assets/icon/search.svg');
background-repeat: no-repeat;
background-position: 15px center;
cursor: not-allowed;
}
`;
19 changes: 14 additions & 5 deletions fe/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,47 @@
import { Theme, css, useTheme } from '@emotion/react';
import { useNavigate } from 'react-router-dom';
import { ReactComponent as MediumLogo } from '../../assets/logo/mediumLogo.svg';
import { ReactComponent as UserImageLargeIcon } from '../../assets/icon/userImageLarge.svg';
import { font } from '../../styles/styles';
import Button from '../common/Button';
import UserImageIcon from '../UserImageIcon';
import ThemeToggle from '../ThemeToggle';
import { useContext } from 'react';
import { UserContext } from '../Context/UserContext';

export default function Header() {
const theme = useTheme();
const navigate = useNavigate();
const { ...context } = useContext(UserContext);

const onLogoClick = () => {
navigate('/issue');
};

const onSignOutClick = () => {
navigate('/sign-in');
const isDarkMode = context.darkMode?.toString();

if (isDarkMode) {
localStorage.clear();
localStorage.setItem('darkMode', isDarkMode);
return;
}

localStorage.clear();
};

return (
<header css={header(theme)}>
<MediumLogo className="logo" onClick={onLogoClick} />

<div className="nav">
<ThemeToggle />
<Button
color={theme.neutral.textStrong}
backgroundColor="inherit"
value="로그아웃"
size="S"
fontSize="S"
onClick={onSignOutClick}
/>
<UserImageLargeIcon />
<UserImageIcon url={context.user?.member.imageUrl} size="M" />
</div>
</header>
);
Expand Down
Loading

0 comments on commit e3612e0

Please sign in to comment.