Skip to content

Commit c37be11

Browse files
authored
Merge pull request #28 from duynd0909/dev
Add Ubuntu deployment plan and enhance submission retrieval
2 parents 8637154 + eceb171 commit c37be11

11 files changed

Lines changed: 1997 additions & 206 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Build & Deploy
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
env:
8+
REGISTRY: ghcr.io
9+
IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}
10+
11+
jobs:
12+
test:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version: 20
19+
cache: npm
20+
- run: npm ci
21+
- run: npm run typecheck
22+
- run: npm run lint
23+
- run: npm run test
24+
25+
build-and-push:
26+
needs: test
27+
runs-on: ubuntu-latest
28+
permissions:
29+
contents: read
30+
packages: write
31+
outputs:
32+
image_tag: ${{ steps.meta.outputs.version }}
33+
steps:
34+
- uses: actions/checkout@v4
35+
36+
- name: Log in to GHCR
37+
uses: docker/login-action@v3
38+
with:
39+
registry: ghcr.io
40+
username: ${{ github.actor }}
41+
password: ${{ secrets.GHCR_TOKEN }}
42+
43+
- name: Docker meta
44+
id: meta
45+
uses: docker/metadata-action@v5
46+
with:
47+
images: ${{ env.IMAGE_PREFIX }}/stackdify-api,${{ env.IMAGE_PREFIX }}/stackdify-web
48+
tags: |
49+
type=sha,prefix=sha-
50+
51+
- name: Build & push API image
52+
uses: docker/build-push-action@v5
53+
with:
54+
context: .
55+
file: apps/api/Dockerfile
56+
push: true
57+
tags: ${{ env.IMAGE_PREFIX }}/stackdify-api:latest,${{ env.IMAGE_PREFIX }}/stackdify-api:sha-${{ github.sha }}
58+
cache-from: type=gha
59+
cache-to: type=gha,mode=max
60+
61+
- name: Build & push Web image
62+
uses: docker/build-push-action@v5
63+
with:
64+
context: .
65+
file: apps/web/Dockerfile
66+
push: true
67+
tags: ${{ env.IMAGE_PREFIX }}/stackdify-web:latest,${{ env.IMAGE_PREFIX }}/stackdify-web:sha-${{ github.sha }}
68+
build-args: NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }}
69+
cache-from: type=gha
70+
cache-to: type=gha,mode=max

apps/web/src/app/problems/[slug]/page.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ import {
7777
import { LabelEdge, type SystemEdgeData } from '@/components/game/LabelEdge';
7878
import { ResultOverlay } from '@/components/game/ResultOverlay';
7979
import { RequirementsSidebar } from '@/components/game/RequirementsSidebar';
80+
import { SubmissionGraphModal } from '@/components/game/SubmissionGraphModal';
8081
import { iconForComponent } from '@/components/game/component-icons';
8182
import {
8283
categoryForComponent,
@@ -697,6 +698,10 @@ export default function ProblemGamePage() {
697698
const [isMinimapVisible, setIsMinimapVisible] = useState(true);
698699
const [slotFeedback, setSlotFeedback] = useState<Record<string, boolean>>({});
699700
const [isGlowMode, setIsGlowMode] = useState(false);
701+
const [viewingSubmission, setViewingSubmission] = useState<{
702+
id: string;
703+
order: number;
704+
} | null>(null);
700705

701706
const {
702707
data: reqGraph,
@@ -1422,8 +1427,10 @@ export default function ProblemGamePage() {
14221427
currentOrder={currentOrder}
14231428
completedOrders={completedOrders}
14241429
isLoading={isGraphLoading}
1430+
token={token}
14251431
onSelectRequirement={handleSelectRequirement}
14261432
onComponentClick={handleComponentClick}
1433+
onViewSubmission={(id, order) => setViewingSubmission({ id, order })}
14271434
/>
14281435
</div>
14291436

@@ -1596,6 +1603,14 @@ export default function ProblemGamePage() {
15961603
onConfirm={handleLeaveConfirm}
15971604
onCancel={handleLeaveCancel}
15981605
/>
1606+
1607+
<SubmissionGraphModal
1608+
submissionId={viewingSubmission?.id ?? null}
1609+
slug={slug}
1610+
token={token}
1611+
componentTypes={problemDetail?.componentTypes ?? []}
1612+
onClose={() => setViewingSubmission(null)}
1613+
/>
15991614
</div>
16001615
);
16011616
}

apps/web/src/app/problems/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ function ProblemCard({
9595

9696
<CardTitle className="mb-2">{problem.title}</CardTitle>
9797
<CardDescription className="line-clamp-2 flex-1">
98-
<div dangerouslySetInnerHTML={{ __html: problem.description }}></div>
98+
<span dangerouslySetInnerHTML={{ __html: problem.description }}></span>
9999
</CardDescription>
100100

101101
{isAuthenticated && problem.requirementCount > 0 && (
@@ -146,7 +146,7 @@ function ProblemListRow({
146146
)}
147147
</div>
148148
<p className="mt-0.5 truncate text-xs text-[var(--text-secondary)]">
149-
{problem.description}
149+
<span dangerouslySetInnerHTML={{ __html: problem.description }}></span>
150150
</p>
151151
</div>
152152

apps/web/src/components/game/LabelEdge.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import type { CSSProperties } from 'react';
44
import { useReducedMotion } from 'motion/react';
5+
import { useTheme } from 'next-themes';
56
import {
67
BaseEdge,
78
EdgeLabelRenderer,
@@ -43,6 +44,8 @@ export function LabelEdge({
4344
animated,
4445
}: EdgeProps) {
4546
const prefersReduced = useReducedMotion();
47+
const { resolvedTheme } = useTheme();
48+
const isDark = resolvedTheme === 'dark';
4649
const edgeData = data as SystemEdgeData | undefined;
4750
const kind = edgeData?.kind ?? 'request';
4851
const isActive = edgeData?.isActive ?? false;
@@ -54,6 +57,7 @@ export function LabelEdge({
5457
const status: SystemEdgeStatus = isActive ? 'active' : edgeData?.status ?? 'normal';
5558
const kindStyle = EDGE_KIND_STYLES[kind];
5659
const stroke = edgeStatusStroke(kind, status);
60+
const labelStroke = edgeStatusStroke(kind, status, isDark);
5761
const shouldAnimate = !prefersReduced && (animated || isActive || kind === 'streaming');
5862

5963
// Separate parallel edges by applying a perpendicular offset
@@ -108,8 +112,8 @@ export function LabelEdge({
108112
position: 'absolute',
109113
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
110114
pointerEvents: 'all',
111-
borderColor: isActive || isHighlighted ? stroke : 'color-mix(in srgb, var(--text-primary) 12%, transparent)',
112-
color: stroke,
115+
borderColor: isActive || isHighlighted ? labelStroke : 'color-mix(in srgb, var(--text-primary) 12%, transparent)',
116+
color: labelStroke,
113117
opacity: isDimmed ? 0.35 : 1,
114118
}}
115119
onMouseEnter={() => edgeData?.onHover?.(id)}

0 commit comments

Comments
 (0)