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

Enhance visual and user experience #214

Merged
merged 13 commits into from
Nov 13, 2024
Merged
2 changes: 1 addition & 1 deletion frontend/components/account/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ function Profile() {
dialogOpen={isDialogOpen}
onDialogOpenChange={manageDialog} // Allow toggling the dialog
text="Update Profile"
className="w-fit bg-btn text-white text-sm py-2 px-4 rounded-md hover:bg-purple-700"
className="w-fit bg-btn text-white text-sm py-2 px-4 rounded-md"
type="button"
variant="primary"
description="Are you sure you want to update your profile?"
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/account/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ function Setting() {
dialogOpen={isUpdateDialogOpen}
onDialogOpenChange={manageUpdateDialog}
text="Update Settings"
className="w-fit bg-btn text-white text-sm py-2 px-4 rounded-md hover:bg-theme-700"
className="w-fit bg-btn text-white text-sm py-2 px-4 rounded-md"
type="button"
variant="primary"
description="Are you sure you want to update your settings?"
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/customs/custom-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function CustomDialogWithButton(props: CustomDialogProps) {
<DialogTitle>
<div className="flex flex-row gap-5">
<ErrorIconDialog />
Warning
{props.text || 'Warning'}
</div>
<span className="h-0.5 w-full bg-slate-200 mt-5"></span>
</DialogTitle>
Expand Down
33 changes: 17 additions & 16 deletions frontend/components/customs/datatable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default function Datatable({
<Table>
<TableHeader>
<TableRow>
<TableHead>{hideIdx ? null : 'No.'}</TableHead>
<TableHead style={{ width: '5%' }}>{hideIdx ? null : 'No.'}</TableHead>
{columns.map((elem) => {
if (elem.isHidden) {
return null
Expand Down Expand Up @@ -129,21 +129,22 @@ export default function Datatable({
<DeleteIcon />
</Button>
)}
{col.customAction && col.customAction.formatter ? (
col.customAction.formatter(elem, router)
) : (
<Button
variant="iconNoBorder"
size="icon"
onClick={() => {
if (col.customAction && col.customAction.onClick) {
col.customAction.onClick(elem)
}
}}
>
{col.customAction?.customActionIcon}
</Button>
)}
{col.customAction &&
(col.customAction.formatter ? (
col.customAction.formatter(elem, router)
) : (
<Button
variant="iconNoBorder"
size="icon"
onClick={() => {
if (col.customAction && col.customAction.onClick) {
col.customAction.onClick(elem)
}
}}
>
{col.customAction?.customActionIcon}
</Button>
))}
</TableCell>
)
}
Expand Down
10 changes: 6 additions & 4 deletions frontend/components/dashboard/new-session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { addUserToMatchmaking } from '../../services/matching-service-api'
import CustomModal from '../customs/custom-modal'
import Loading from '../customs/loading'
import { capitalizeFirstLowerRest } from '@/util/string-modification'
import { encodeStr } from '@/util/encryption'

export const NewSession = () => {
const router = useRouter()
Expand Down Expand Up @@ -82,7 +83,7 @@ export const NewSession = () => {

// Refactor this
const wsUrl = (process.env.NEXT_PUBLIC_API_URL || 'ws://localhost:3006')?.concat(
`/matching/ws/?id=${websocketId}`
`/api/matching/ws/?id=${websocketId}`
)
const socket = new WebSocket(wsUrl)
setTimeout(() => {
Expand All @@ -109,7 +110,8 @@ export const NewSession = () => {
switch (newMessage.type) {
case WebSocketMessageType.SUCCESS:
updateMatchmakingStatus(MatchingStatus.MATCH_FOUND, newMessage.matchId)
router.push(`/code/${newMessage.matchId}`)
const encodedId = encodeStr(newMessage.matchId)
router.push(`/code/${encodedId}`)
break
case WebSocketMessageType.FAILURE:
socketRef.current?.close()
Expand Down Expand Up @@ -204,7 +206,7 @@ export const NewSession = () => {
</SelectContent>
</Select>
</div>
<Button className="mt-4 bg-purple-600 hover:bg-[#A78BFA]" onClick={handleMatchmaking}>
<Button variant="primary" onClick={handleMatchmaking}>
Start matchmaking
</Button>

Expand Down Expand Up @@ -267,7 +269,7 @@ export const NewSession = () => {
<Button
variant={'ghostTabLabel'}
size={'lg'}
onClick={() => void router.push(`/code/${modalData.matchId}`)}
onClick={() => void router.push(`/code/${encodeStr(modalData.matchId)}`)}
>
Proceed to coding session
</Button>
Expand Down
9 changes: 7 additions & 2 deletions frontend/components/dashboard/resume-session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Button } from '../ui/button'
import { IMatch } from '@repo/user-types'
import { convertSortedComplexityToComplexity } from '@repo/question-types'
import { capitalizeFirstLowerRest } from '@/util/string-modification'
import { encodeStr } from '@/util/encryption'
import { toast } from 'sonner'

interface IResumeSessionProps {
match: IMatch
Expand All @@ -18,8 +20,11 @@ export default function ResumeSession({ match, isOngoing }: IResumeSessionProps)
try {
const ongoing = await isOngoing()
if (!ongoing) return
router.push(`/code/${match.id}`)
} catch (error) {}
const encodedId = encodeStr(match.id)
router.push(`/code/${encodedId}`)
} catch (error) {
toast.error('Unable to resume session due to a server error')
}
}

return (
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'

const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-300 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
Expand All @@ -16,7 +16,7 @@ const buttonVariants = cva(
link: 'text-primary underline-offset-4 hover:underline',
icon: 'bg-transparent border-[1px] rounded-xl hover:bg-btn-secondaryHover',
iconNoBorder: 'hover:bg-btn-hover',
primary: 'bg-theme-600 hover:bg-theme-700 text-primary-foreground',
primary: 'bg-theme-600 hover:bg-theme-700 text-primary-foreground focus-visible:ring-gray-500',
activeTab:
'text-foreground bg-transparent hover:bg-btn-hover rounded-b-none border-b-2 border-theme-600',
ghostTab: 'text-foreground bg-transparent hover:bg-btn-hover rounded-b-none',
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
'fixed inset-0 z-50 bg-slate-200/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
'fixed inset-0 z-[995] bg-slate-200/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className
)}
{...props}
Expand All @@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-3xl translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-7 px-10 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
'fixed left-[50%] top-[50%] z-[995] grid w-full max-w-3xl translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-7 px-10 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
'flex flex-grow flex-col h-1/4 bg-white bg-opacity-100',
className
)}
Expand Down
2 changes: 1 addition & 1 deletion frontend/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default NextAuth({

try {
const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3002',
baseURL: process.env.NEXT_PUBLIC_API_URL?.concat('/api') ?? 'http://localhost:3002',
})

const response = await api.post('/auth/login', { usernameOrEmail: username, password })
Expand Down
10 changes: 7 additions & 3 deletions frontend/pages/code/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { getChatHistory, getCollabHistory } from '@/services/collaboration-servi
import ReadOnlyCodeMirrorEditor from '../read-only-editor'
import { ResultModel } from '@repo/collaboration-types'
import { capitalizeFirstLowerRest } from '@/util/string-modification'
import { decodeStr } from '@/util/encryption'

const formatQuestionCategories = (cat: Category[]) => {
return cat.map((c) => capitalizeFirstLowerRest(c)).join(', ')
Expand All @@ -40,7 +41,7 @@ const formatQuestionCategories = (cat: Category[]) => {
export default function Code() {
const router = useRouter()
const [isChatOpen, setIsChatOpen] = useState(true)
const { id } = router.query
const [id, setId] = useState(router.query.id as string)
const editorRef = useRef<{ getText: () => string } | null>(null)
const [editorLanguage, setEditorLanguage] = useState<LanguageMode>(LanguageMode.Javascript)
const testTabs = ['Testcases', 'Test Results']
Expand Down Expand Up @@ -81,8 +82,10 @@ export default function Code() {
const { data: sessionData } = useSession()

useEffect(() => {
const matchId = router.query.id as string
const encodedId = router.query.id as string
const matchId = decodeStr(encodedId)
retrieveMatchDetails(matchId)
setId(matchId)
}, [router.query.id, retry])

useEffect(() => {
Expand Down Expand Up @@ -136,6 +139,7 @@ export default function Code() {
socketRef.current.on('disconnect', () => {
if (!isViewOnly) {
router.push('/')
toast.info('The session has ended')
}
})

Expand Down Expand Up @@ -201,7 +205,7 @@ export default function Code() {

function handleEndSessionConfirmation() {
if (socketRef.current) {
socketRef.current?.emit('end-session')
socketRef.current.emit('end-session')
router.push('/')
}
setIsDialogOpen(false)
Expand Down
2 changes: 1 addition & 1 deletion frontend/pages/code/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const CodeMirrorEditor = forwardRef(({ roomId, language }: IProps, ref) => {
const token = session.user.accessToken
if (!token) return undefined
const wsProvider = new WebsocketProvider(
process.env.NEXT_PUBLIC_API_URL?.concat('/collab/y/ws') ?? 'ws://localhost:3008',
process.env.NEXT_PUBLIC_API_URL?.concat('/api/collab/y/ws') ?? 'ws://localhost:3008',
roomId,
ydoc,
{
Expand Down
6 changes: 2 additions & 4 deletions frontend/pages/questions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,8 @@ export default function Questions() {
} else if (modificationType === Modification.DELETE) {
if (!questionData.id) return
try {
const res = await deleteQuestionById(questionData.id)
if (res) {
toast.success('Question deleted successfully')
}
await deleteQuestionById(questionData.id)
toast.success('Question deleted successfully')
} catch (error) {
toast.error('Failed to delete question' + error)
return
Expand Down
3 changes: 0 additions & 3 deletions frontend/pages/questions/props.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ const getColumns = (isAdmin: boolean): IDatatableColumn[] => {
},
{
key: 'title',
width: '30%',
offAutoCapitalize: true,
},
{
key: 'categories',
width: '40%',
formatter: (values) => {
const c = values.map((v: string) => (
<CustomLabel
Expand Down Expand Up @@ -72,7 +70,6 @@ const getColumns = (isAdmin: boolean): IDatatableColumn[] => {
},
{
key: 'complexity',
width: '10%',
isSortable: true,
formatter: (value) => {
return <DifficultyLabel complexity={value} />
Expand Down
4 changes: 3 additions & 1 deletion frontend/pages/sessions/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DifficultyLabel } from '@/components/customs/difficulty-label'
import { Button } from '@/components/ui/button'
import CustomLabel from '@/components/ui/label'
import { IDatatableColumn, IRowData } from '@/types'
import { encodeStr } from '@/util/encryption'
import { capitalizeFirstLowerRest } from '@/util/string-modification'
import { EyeIcon } from 'lucide-react'
import { NextRouter } from 'next/router'
Expand Down Expand Up @@ -77,7 +78,8 @@ export const columns: IDatatableColumn[] = [
variant="iconNoBorder"
size="icon"
onClick={() => {
router.push(`/code/${elem._id}`)
const encoded = encodeStr(elem._id)
router.push(`/code/${encoded}`)
}}
>
{elem.isCompleted ? <EyeIcon /> : <PlayIcon />}
Expand Down
4 changes: 3 additions & 1 deletion frontend/pages/sessions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Button } from '@/components/ui/button'
import ConfirmDialog, { ConfirmDialogProps } from '@/components/customs/confirm-dialog'
import { getOngoingMatch } from '@/services/matching-service-api'
import { useRouter } from 'next/router'
import { encodeStr } from '@/util/encryption'

export default function Sessions() {
const router = useRouter()
Expand Down Expand Up @@ -111,7 +112,8 @@ export default function Sessions() {
if (!session?.user?.id) return
const matchData = await getOngoingMatch(session.user.id)
if (matchData) {
router.push(`/code/${matchData.id}`)
const encodedId = encodeStr(matchData.id)
router.push(`/code/${encodedId}`)
} else {
setDialog((prev) => ({ ...prev, dialogData: { ...prev.dialogData, isOpen: true } }))
}
Expand Down
10 changes: 6 additions & 4 deletions frontend/services/axios-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ const createServiceAPI = (baseURL: string) => {
return api
}

const userServiceAPI = createServiceAPI(process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3002')
const questionServiceAPI = createServiceAPI(process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3004')
const matchingServiceAPI = createServiceAPI(process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3006')
const collaborationServiceAPI = createServiceAPI(process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3008')
const userServiceAPI = createServiceAPI(process.env.NEXT_PUBLIC_API_URL?.concat('/api') ?? 'http://localhost:3002')
const questionServiceAPI = createServiceAPI(process.env.NEXT_PUBLIC_API_URL?.concat('/api') ?? 'http://localhost:3004')
const matchingServiceAPI = createServiceAPI(process.env.NEXT_PUBLIC_API_URL?.concat('/api') ?? 'http://localhost:3006')
const collaborationServiceAPI = createServiceAPI(
process.env.NEXT_PUBLIC_API_URL?.concat('/api') ?? 'http://localhost:3008'
)

export default { userServiceAPI, questionServiceAPI, matchingServiceAPI, collaborationServiceAPI }
8 changes: 8 additions & 0 deletions frontend/util/encryption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const encodeStr = (str: string) => {
return btoa(str)
}

export const decodeStr = (encoded: string) => {
if (!encoded) return ''
return atob(encoded)
}
4 changes: 4 additions & 0 deletions nginx/templates/local-nginx.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ http {
proxy_set_header Upgrade ${D}http_upgrade;
proxy_set_header Connection "upgrade";

location /api/ {
rewrite ^/api(/.*)$ $1 last;
}

location /auth {
proxy_pass ${USER_SERVICE_URL};
}
Expand Down
Loading