Merge branch 'fe/designs' into 'frontend'

Fe/designs

See merge request s11-webmobile1-sub2/S11P12A701!56
This commit is contained in:
조민우 2024-08-05 17:20:11 +09:00
commit 16a0c8740d
13 changed files with 81 additions and 33 deletions

View File

@ -0,0 +1,8 @@
<svg width="49" height="49" viewBox="0 0 49 49" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Compass">
<g id="Icon">
<path d="M24.5 44.5C35.5457 44.5 44.5 35.5457 44.5 24.5C44.5 13.4543 35.5457 4.5 24.5 4.5C13.4543 4.5 4.5 13.4543 4.5 24.5C4.5 35.5457 13.4543 44.5 24.5 44.5Z" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M32.98 16.02L28.74 28.74L16.02 32.98L20.26 20.26L32.98 16.02Z" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 510 B

View File

@ -0,0 +1,5 @@
<svg width="49" height="49" viewBox="0 0 49 49" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Plus circle">
<path id="Icon" d="M24.5 16.5V32.5M16.5 24.5H32.5M44.5 24.5C44.5 35.5457 35.5457 44.5 24.5 44.5C13.4543 44.5 4.5 35.5457 4.5 24.5C4.5 13.4543 13.4543 4.5 24.5 4.5C35.5457 4.5 44.5 13.4543 44.5 24.5Z" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 395 B

View File

@ -1,5 +1,6 @@
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import styles from './ClassCard.module.css'; import styles from './ClassCard.module.css';
import CompassIcon from '/src/assets/icons/compass.svg?react';
export default function ClassCard({ img, path, children }) { export default function ClassCard({ img, path, children }) {
return ( return (
@ -7,11 +8,17 @@ export default function ClassCard({ img, path, children }) {
to={path} to={path}
className={styles.card} className={styles.card}
> >
{img ? (
<img <img
src={img} src={img}
alt="강의 이미지" alt="강의 이미지"
className={styles.thumbnail} className={styles.thumbnail}
/> />
) : (
<div className={styles.thumbnail}>
<CompassIcon />
</div>
)}
<div>{children}</div> <div>{children}</div>
</Link> </Link>
); );

View File

@ -9,9 +9,13 @@
} }
.thumbnail { .thumbnail {
display: flex;
justify-content: center;
align-items: center;
width: 295px; width: 295px;
height: 220px; height: 220px;
border-radius: 20px; border-radius: 20px;
background-color: var(--background-secondary); background-color: var(--background-secondary);
stroke: var(--text-color);
box-sizing: border-box; box-sizing: border-box;
} }

View File

@ -115,7 +115,10 @@ export default function LiveRoom() {
</FocusLayoutContainer> </FocusLayoutContainer>
</div> </div>
)} )}
<ControlBar controls={{ chat: false, leave: false, screenShare: role === '강사' }} /> <ControlBar
variation="minimal"
controls={{ chat: false, leave: true, screenShare: role === '강사' }}
/>
</div> </div>
<ChatRoom /> <ChatRoom />
</LayoutContextProvider> </LayoutContextProvider>

View File

@ -18,7 +18,7 @@
.groupList { .groupList {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 8px;
list-style: none; list-style: none;
padding: 0; padding: 0;
margin: 0; margin: 0;

View File

@ -15,5 +15,4 @@
.active { .active {
font-weight: 700; font-weight: 700;
text-decoration: underline;
} }

View File

@ -1,9 +1,9 @@
import { useQuery } from '@tanstack/react-query'; import { useSuspenseQuery } from '@tanstack/react-query';
import instance from '../../utils/axios/instance'; import instance from '../../utils/axios/instance';
import { API_URL } from '../../constants'; import { API_URL } from '../../constants';
export function useNotices(lectureId, page = 0) { export function useNotices(lectureId, page = 0) {
return useQuery({ return useSuspenseQuery({
queryKey: ['noticelist', lectureId, page], queryKey: ['noticelist', lectureId, page],
queryFn: () => instance.get(`${API_URL}/board?lectureId=${lectureId}&category=announcement&pageNo=${page}`), queryFn: () => instance.get(`${API_URL}/board?lectureId=${lectureId}&category=announcement&pageNo=${page}`),
}); });

View File

@ -1,31 +1,29 @@
import { LiveRoom } from '../../components/LiveRoom'; import { LiveRoom } from '../../components/LiveRoom';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useCallback, useEffect } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { LiveKitRoom } from '@livekit/components-react'; import { LiveKitRoom } from '@livekit/components-react';
import instance from '../../utils/axios/instance'; import instance from '../../utils/axios/instance';
import { API_URL, ROOM_URL } from '../../constants'; import { API_URL, ROOM_URL } from '../../constants';
import useBoundStore from '../../store';
import '@livekit/components-styles'; import '@livekit/components-styles';
import LoadingIndicator from '../../components/LoadingIndicator.jsx/LoadingIndicator'; import LoadingIndicator from '../../components/LoadingIndicator.jsx/LoadingIndicator';
export default function LivePage() { export default function LivePage() {
const { roomId } = useParams(); const { roomId } = useParams();
const [liveToken, setLiveToken] = useState(null);
const generateToken = useCallback(async () => { const generateToken = useCallback(async () => {
await instance.post(`${API_URL}/video/makeroom/${roomId}`); await instance.post(`${API_URL}/video/makeroom/${roomId}`);
const { data } = await instance.post(`${API_URL}/video/joinroom/${roomId}`); const { data } = await instance.post(`${API_URL}/video/joinroom/${roomId}`).catch(() => {
alert('방에 입장할 수 없습니다.');
});
return data.token; return data.token;
}, [roomId]); }, [roomId]);
const liveToken = useBoundStore((state) => state.liveToken);
useEffect(() => { useEffect(() => {
if (!liveToken) {
generateToken().then((token) => { generateToken().then((token) => {
useBoundStore.setState({ liveToken: token }); setLiveToken(token);
}); });
} }, [generateToken]);
}, [generateToken, liveToken]);
return liveToken ? ( return liveToken ? (
<LiveKitRoom <LiveKitRoom
@ -33,6 +31,9 @@ export default function LivePage() {
serverUrl={ROOM_URL} serverUrl={ROOM_URL}
connect={true} connect={true}
data-lk-theme="default" data-lk-theme="default"
onDisconnected={() => {
instance.post(`${API_URL}/video/deleteroom/${roomId}`).catch(() => {});
}}
> >
<LiveRoom /> <LiveRoom />
</LiveKitRoom> </LiveKitRoom>

View File

@ -1,17 +1,19 @@
import styles from './TeacherHomePage.module.css';
import { Link } from 'react-router-dom';
import { ClassCard } from '../../components/ClassCard'; import { ClassCard } from '../../components/ClassCard';
import { ClassGrid } from '../../components/ClassGrid'; import { ClassGrid } from '../../components/ClassGrid';
import { MaxWidthLayout } from '../../components/Layout'; import { MaxWidthLayout } from '../../components/Layout';
import { useMyLectures } from '../../hooks/api/useMyLectures'; import { useMyLectures } from '../../hooks/api/useMyLectures';
import PlusIcon from '/src/assets/icons/plus.svg?react';
export default function TeacherHomePage() { export default function TeacherHomePage() {
const { data: myLectures } = useMyLectures(); const { data: myLectures } = useMyLectures();
const onGoingClasses = myLectures?.data ?? []; const onGoingClasses = myLectures?.data ?? [];
console.log(onGoingClasses);
// TODO: ,
return ( return (
<MaxWidthLayout> <MaxWidthLayout>
<ClassGrid title="내 강의"> <ClassGrid title="내 강의">
{onGoingClasses.map?.((lecture) => ( {onGoingClasses.map((lecture) => (
<ClassCard <ClassCard
key={lecture.id} key={lecture.id}
path={`/lecture/${lecture.id}`} path={`/lecture/${lecture.id}`}
@ -20,7 +22,13 @@ export default function TeacherHomePage() {
{lecture.title} {lecture.title}
</ClassCard> </ClassCard>
))} ))}
<ClassCard path={'/lecture/create'}> 강의 만들기</ClassCard> <Link
to={'/lecture/create'}
className={styles.add}
>
<PlusIcon />
<span> 강의 만들기</span>
</Link>
</ClassGrid> </ClassGrid>
</MaxWidthLayout> </MaxWidthLayout>
); );

View File

@ -0,0 +1,21 @@
.add {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 20px;
margin-bottom: 32px;
background-color: var(--background);
color: var(--text-color);
stroke: var(--text-color);
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 1.4;
border-radius: 20px;
transition: background-color 0.2s ease;
&:hover {
background-color: var(--background-secondary);
}
}

View File

@ -3,7 +3,6 @@ import { userTypeSlice } from './userTypeSlice';
import { tokenSlice } from './tokenSlice'; import { tokenSlice } from './tokenSlice';
import { userNameSlice } from './userNameSlice'; import { userNameSlice } from './userNameSlice';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import { liveSlice } from './liveSlice';
const useBoundStore = create( const useBoundStore = create(
persist( persist(
@ -11,7 +10,6 @@ const useBoundStore = create(
...userTypeSlice(...a), ...userTypeSlice(...a),
...tokenSlice(...a), ...tokenSlice(...a),
...userNameSlice(...a), ...userNameSlice(...a),
...liveSlice(...a),
}), }),
{ name: 'bound-store' } { name: 'bound-store' }
) )

View File

@ -1,6 +0,0 @@
export const liveSlice = (set) => ({
liveToken: null,
setLiveToken: (liveToken) => set({ liveToken }),
participants: 0,
setParticipants: (participants) => set({ participants }),
});