Skip to content
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 15 additions & 0 deletions apps/level_up/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# NextAuth Configuration
NEXTAUTH_URL=http://localhost:3001
NEXTAUTH_SECRET=your-nextauth-secret-here

# SSC OAuth Configuration
SSC_CLIENT_ID=your-ssc-client-id

#Game craft ssc event id
GAME_CRAFT_SSC_EVENT_ID=202501

# External App URLs
# Development: http://localhost:3000
# Production: https://ceit-ssc.ir
NEXT_PUBLIC_SSC_URL=http://localhost:3000
NEXT_PUBLIC_API_URL=https://localhost:8000
26 changes: 26 additions & 0 deletions apps/level_up/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Step 1: Use a lightweight Node.js base
FROM node:20-alpine AS base

# Step 2: Set working directory
WORKDIR /app

# Step 3: Copy only dependency files first (for caching)
COPY pnpm-lock.yaml ./
COPY package.json ./
COPY pnpm-workspace.yaml ./
COPY apps/game_craft/package.json apps/game_craft/
COPY packages/core/package.json packages/core/
COPY packages/ui/package.json packages/ui/

# Step 4: Install deps with pnpm (monorepo-aware)
RUN npm install -g pnpm@9 && pnpm install --frozen-lockfile

# Step 5: Copy all source code
COPY . .

# Step 6: Build game_craft
RUN pnpm run build

# Step 7: Start production server
EXPOSE 3000
CMD ["pnpm", "start"]
153 changes: 153 additions & 0 deletions apps/level_up/app/[locale]/(public)/faq/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
"use client";

import { Flex, theme, Typography, Collapse, Space, Card } from "antd";
import { useTranslations } from "next-intl";
import { QuestionCircleOutlined } from "@ant-design/icons";
import { useResponsive } from "lib/hooks/useResponsive";

const { useToken } = theme;

export default function FAQPage() {
const { token } = useToken();
const screens = useResponsive();
const t = useTranslations("app");
const faqViewPadding = screens.lg ? "3rem 5rem" : "3rem 2rem";

const items = [
{
key: "1",
label: (
<Space>
<QuestionCircleOutlined style={{ color: token.colorPrimary }} />
<Typography.Text strong style={{ fontSize: "16px" }}>
{t("faq.competitionParticipate.title")}
</Typography.Text>
</Space>
),
children: (
<Typography.Paragraph style={{ fontSize: "16px", lineHeight: 1.6 }}>
{t("faq.competitionParticipate.content")}
</Typography.Paragraph>
),
},
{
key: "2",
label: (
<Space>
<QuestionCircleOutlined style={{ color: token.colorPrimary }} />
<Typography.Text strong style={{ fontSize: "16px" }}>
{t("faq.teamsConditions.title")}
</Typography.Text>
</Space>
),
children: (
<Typography.Paragraph style={{ fontSize: "16px", lineHeight: 1.6 }}>
{t("faq.teamsConditions.content")}
</Typography.Paragraph>
),
},
{
key: "3",
label: (
<Space>
<QuestionCircleOutlined style={{ color: token.colorPrimary }} />
<Typography.Text strong style={{ fontSize: "16px" }}>
{t("faq.howToParticipate.title")}
</Typography.Text>
</Space>
),
children: (
<Typography.Paragraph style={{ fontSize: "16px", lineHeight: 1.6 }}>
{t("faq.howToParticipate.content")}
</Typography.Paragraph>
),
},
{
key: "4",
label: (
<Space>
<QuestionCircleOutlined style={{ color: token.colorPrimary }} />
<Typography.Text strong style={{ fontSize: "16px" }}>
{t("faq.competitionRules.title")}
</Typography.Text>
</Space>
),
children: (
<Typography.Paragraph style={{ fontSize: "16px", lineHeight: 1.6 }}>
{t("faq.competitionRules.content")}
</Typography.Paragraph>
),
}
];

return (
<Flex
align="center"
justify="center"
vertical
style={{
flex: 1,
width: "100%",
}}
>
<Flex
align="center"
justify="center"
style={{
width: "100%",
backgroundSize: "fit",
backgroundPosition: "center",
padding: faqViewPadding,
position: "relative",
}}
>
<Card
style={{
backgroundColor: token.colorBgBase,
width: "100%",
maxWidth: "1000px",
borderRadius: token.borderRadiusLG,
boxShadow: token.boxShadow,
border: "none",
}}
>
<Flex vertical align="center" gap="large" style={{ width: "100%" }}>
<Space direction="vertical" align="center" size={16}>
<Typography.Title
level={1}
style={{
fontWeight: 900,
color: token.colorPrimary,
textAlign: "center",
margin: 0,
}}
>
{t("faq.title")}
</Typography.Title>
<Typography.Text
type="secondary"
style={{
fontSize: "18px",
textAlign: "center",
}}
>
{t("faq.competitionConditions")}
</Typography.Text>
</Space>

<Collapse
items={items}
ghost
size="large"
style={{
width: "100%",
backgroundColor: "transparent",
}}
expandIconPosition="end"
/>
</Flex>
</Card>
</Flex>
</Flex>
);
}
96 changes: 96 additions & 0 deletions apps/level_up/app/[locale]/(public)/gallery/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"use client";

import { Empty, Flex, theme, Typography, Image, Row, Col } from "antd";
import { useTranslations } from "next-intl";
import { useResponsive } from "../../../../lib/hooks/useResponsive";
import { galleryImages } from "../../../../config/galleryImages";

const { useToken } = theme;

export default function GalleryPage() {
const { token } = useToken();
const screens = useResponsive();
const t = useTranslations("app");
const galleryViewPadding = screens.lg ? "3rem 5rem" : "3rem 2rem";

return (
<Flex
vertical
align="center"
justify="center"
style={{
flex: 1,
width: "100%",
minHeight: "100%",
}}
>
<Flex
vertical
align="center"
justify="center"
style={{
width: "100%",
padding: galleryViewPadding,
}}
>
<Typography.Title style={{ color: "white" }}>
{t("mainNavigation.gallery")}
</Typography.Title>
<Flex
vertical
align="center"
justify="center"
style={{
width: "100%",
minHeight: "200px",
backgroundColor: token.colorBgBase,
borderRadius: token.borderRadius,
padding: token.padding,
}}
>
{galleryImages.length > 0 ? (
<Image.PreviewGroup>
<Row
gutter={[16, 16]}
style={{ width: "100%" }}
align={"middle"}
justify="center"
>
{galleryImages.map((image) => (
<Col key={image.id} xs={24} sm={12} md={8} lg={6} xl={4}>
<Flex
key={image.id}
align="center"
justify="center"
style={{
width: "100%",
borderRadius: token.borderRadius,
overflow: "hidden",
}}
>
<Image
src={image.src}
alt={image.alt}
style={{
width: "100%",
height: "300px",
objectFit: "cover",
cursor: "pointer",
}}
preview={{
src: image.src,
}}
/>
</Flex>
</Col>
))}
</Row>
</Image.PreviewGroup>
) : (
<Empty description="No image yet" />
)}
</Flex>
</Flex>
</Flex>
);
}
55 changes: 55 additions & 0 deletions apps/level_up/app/[locale]/(public)/history/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"use client";

import { Empty, Flex, theme, Typography } from "antd";
import { useTranslations } from "next-intl";
import {useResponsive} from "../../../../lib/hooks/useResponsive";

const { useToken } = theme;

export default function HistoryPage() {
const { token } = useToken();
const screens = useResponsive();
const t = useTranslations("app");
const historyViewPadding = screens.lg ? "3rem 5rem" : "3rem 2rem";

return (
<Flex
vertical
align="center"
justify="center"
style={{
flex: 1,
width: "100%",
minHeight: "100%",
}}
>
<Flex
vertical
align="center"
justify="center"
style={{
width: "100%",
padding: historyViewPadding,
}}
>
<Typography.Title style={{ color: "white" }}>
{t("mainNavigation.history")}
</Typography.Title>
<Flex
vertical
align="center"
justify="center"
style={{
width: "100%",
minHeight: "200px",
backgroundColor: token.colorBgBase,
borderRadius: token.borderRadius,
padding: token.padding,
}}
>
<Empty description="No history yet" />
</Flex>
</Flex>
</Flex>
);
}
48 changes: 48 additions & 0 deletions apps/level_up/app/[locale]/(public)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"use client";

import { FloatButton, Layout, theme } from "antd";
import { AppHeader } from "../../../components/layout/AppHeader";
import { AppFooter } from "../../../components/layout/AppFooter";
import Wave from "../../../components/common/Wave";
import { usePathname } from "next/navigation";

interface MainLayoutProps {
children: React.ReactNode;
}

const { useToken } = theme;

export default function MainLayout({ children }: MainLayoutProps) {
const { token } = useToken();
const pathname = usePathname();

// if pathname == /[locale]/ --> home page
const isHomePage = pathname === "/" || /^\/[a-zA-Z-]+\/?$/.test(pathname);

return (
<Layout
style={{ minHeight: "100vh", display: "flex", flexDirection: "column" }}
>
<AppHeader />
<Layout.Content
style={{
flex: 1,
display: "flex",
flexDirection: "column",
backgroundColor: isHomePage ? token.colorBgBase : token.colorPrimary,
backgroundImage: isHomePage ? null : "url('/images/pattern.png')",
}}
>
{children}
<Wave
width="100%"
height="auto"
fill={token.colorPrimary}
style={{ transform: "scaleY(-1) translateY(-2px)" }}
/>
</Layout.Content>
<AppFooter />
{/*<FloatButton.BackTop style={{ insetInlineStart: 24 }} />*/}
</Layout>
);
}
Loading
Loading