Skip to content

Commit

Permalink
feat: fix merge conflicts [gh-36]
Browse files Browse the repository at this point in the history
  • Loading branch information
jakubfolta committed Jul 4, 2024
2 parents d83b447 + 3ce6a33 commit 5fec170
Show file tree
Hide file tree
Showing 84 changed files with 1,716 additions and 289 deletions.
2 changes: 1 addition & 1 deletion .envexample
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ FUSIONAUTH_URL: http://fusionauth:9011
FUSIONAUTH_APPLICATION_ID: 23e4b229-1219-42e5-aed6-f9b6f1eedef8

# Frontend
API_BASE_URL=http://proxy/cpf/api
NEXT_PUBLIC_API_BASE_URL: http://proxy/cpf/api
55 changes: 55 additions & 0 deletions .github/workflows/trivy-frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Scan Frontend with Trivy

on:
workflow_dispatch:
pull_request:
types:
- opened
- synchronize
- reopened
- ready_for_review
branches:
- main
- develop

env:
IMAGE_NAME: frontend
VERSION: v1

jobs:
build_docker_image:
name: Build docker image
timeout-minutes: 15
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Build docker image
run: docker build . --file Dockerfile --tag $IMAGE_NAME

- name: Log in to gHRC
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin

- name: Push image
run: |
docker tag $IMAGE_NAME ghcr.io/tivix/cpf/$IMAGE_NAME:$VERSION
docker push ghcr.io/tivix/cpf/$IMAGE_NAME:$VERSION
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.23.0
with:
image-ref: "ghcr.io/tivix/cpf/${{ env.IMAGE_NAME }}:${{ env.VERSION }}"
scanners: "vuln,secret,config"
format: "sarif"
output: "trivy-fe-results.sarif"
severity: "CRITICAL,HIGH"

- name: Upload scan result to Github Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-fe-results.sarif
category: "image"
10 changes: 9 additions & 1 deletion frontend/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
{
"extends": ["next/core-web-vitals", "plugin:prettier/recommended", "plugin:sonarjs/recommended"],
"extends": [
"next/core-web-vitals",
"plugin:sonarjs/recommended",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"settings": {
"next": {
"rootDir": "/frontend"
Expand Down
3 changes: 2 additions & 1 deletion frontend/.husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# hooks start from the project root directory
cd frontend

yarn lint:fix
yarn lint:fix && yarn format
yarn compile
2 changes: 1 addition & 1 deletion frontend/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const nextConfig = {
hostname: 'tailwindui.com',
pathname: '/img/logos/mark.svg',
}],
}
},
};

export default nextConfig;
3 changes: 3 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"test": "echo \"tests\"",
"lint": "next lint",
"lint:fix": "next lint --fix",
"format": "prettier 'src/**/*.{js,jsx,ts,tsx}' --write",
"postinstall": "cd .. && husky frontend/.husky"
},
"dependencies": {
Expand All @@ -18,6 +19,7 @@
"next": "14.1.3",
"react": "^18",
"react-dom": "^18",
"react-easy-crop": "^5.0.7",
"react-markdown": "^9.0.1",
"react-tooltip": "^5.26.3",
"remark-gfm": "^4.0.0",
Expand All @@ -28,6 +30,7 @@
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.2.4",
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/api/bucket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { mapKeysToCamelCase } from '@app/utils';
import { API_URLS } from '.';
import { Bucket } from '@app/types/library';

async function getBucketDetails(slug: string) {
const response = await fetch(`${API_URLS.library.buckets}/${slug}`);

if (!response.ok) {
throw new Error('Failed to fetch bucket details');
}
const data = await response.json();

return mapKeysToCamelCase<Bucket>(data);
}

export { getBucketDetails };
2 changes: 1 addition & 1 deletion frontend/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const baseUrl = process.env.API_BASE_URL;
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;

export const API_URLS = {
library: {
Expand Down
45 changes: 45 additions & 0 deletions frontend/src/api/ladder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { mapKeysToCamelCase } from '@app/utils';
import { API_URLS } from '.';
import { LadderCardInterface } from '@app/components/common/LadderCard';
import { LadderBand } from '@app/types/library';

async function getLadders() {
const response = await fetch(API_URLS.library.ladders);

if (!response.ok) {
throw new Error('Failed to fetch ladders');
}
const data = await response.json();

return mapKeysToCamelCase<LadderCardInterface[]>(data);
}

async function getLadderDetails(slug: string) {
const response = await fetch(`${API_URLS.library.ladders}/${slug}`);

if (!response.ok) {
throw new Error('Failed to fetch ladder details');
}
const data = await response.json();

return mapKeysToCamelCase<{
ladderName: string;
bands: Record<string, LadderBand>;
}>(data);
}

async function getLadderName(slug: string) {
const response = await fetch(`${API_URLS.library.ladders}/${slug}`);

if (!response.ok) {
throw new Error('Failed to fetch ladder details');
}
const data = await response.json();

return mapKeysToCamelCase<{
ladderName: string;
bands: Record<string, LadderBand>;
}>(data).ladderName;
}

export { getLadders, getLadderDetails, getLadderName };
11 changes: 11 additions & 0 deletions frontend/src/app/(app)/documentation/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Typography } from '@app/components/common/Typography';

export default function Documentation() {
return (
<div>
<Typography className="mb-10" variant="body-l/semibold" as="h1">
Documentation
</Typography>
</div>
);
}
35 changes: 8 additions & 27 deletions frontend/src/app/(app)/library/[ladder]/[bucket]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,8 @@
import { Breadcrumbs } from '@app/components/modules/Breadcrumbs';
import { mapKeysToCamelCase } from '@app/utils';
import { BucketDetails } from '@app/components/modules/BucketDetails';
import { API_URLS } from '@app/api';

async function getBucketDetails(slug: string) {
const response = await fetch(`${API_URLS.library.buckets}/${slug}`);

if (!response.ok) {
throw new Error('Failed to fetch bucket details');
}
const data = await response.json();

return mapKeysToCamelCase(data);
}

async function getLadderName(slug: string) {
const response = await fetch(`${API_URLS.library.ladders}/${slug}`);

if (!response.ok) {
throw new Error('Failed to fetch ladder details');
}
const data = await response.json();

return mapKeysToCamelCase(data).ladderName;
}
import { getLadderName } from '@app/api/ladder';
import { getBucketDetails } from '@app/api/bucket';
import { routes } from '@app/constants';

export default async function BucketDetailed({ params }: { params: { bucket: string; ladder: string } }) {
const { bucket, ladder } = params;
Expand All @@ -34,12 +13,14 @@ export default async function BucketDetailed({ params }: { params: { bucket: str
<div>
<Breadcrumbs
breadcrumbs={[
{ label: 'CPF Library', href: '/library', current: false },
{ label: ladderName, href: `/library/${ladder}`, current: false },
{ label: data.bucketName, href: `/library/${ladder}/${bucket}`, current: true },
{ label: 'CPF Library', href: routes.library.index, current: false },
{ label: ladderName, href: `${routes.library.index}/${ladder}`, current: false },
{ label: data.bucketName, href: `${routes.library.index}/${ladder}/${bucket}`, current: true },
]}
/>
{data && <BucketDetails data={data} />}
</div>
);
}

export const dynamic = 'force-dynamic';
23 changes: 7 additions & 16 deletions frontend/src/app/(app)/library/[ladder]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
import { Breadcrumbs } from '@app/components/modules/Breadcrumbs';
import { LibraryDetailed } from '@app/components/modules/LibraryDetailed';
import { mapKeysToCamelCase } from '@app/utils';
import { API_URLS } from '@app/api';

async function getLadderDetails(slug: string) {
const response = await fetch(`${API_URLS.library.ladders}/${slug}`);

if (!response.ok) {
throw new Error('Failed to fetch ladder details');
}
const data = await response.json();

return mapKeysToCamelCase(data);
}
import { getLadderDetails } from '@app/api/ladder';
import { routes } from '@app/constants';

export default async function LadderDetailed({ params }: { params: { ladder: string } }) {
const data = await getLadderDetails(params.ladder);
Expand All @@ -21,11 +10,13 @@ export default async function LadderDetailed({ params }: { params: { ladder: str
<div>
<Breadcrumbs
breadcrumbs={[
{ label: 'CPF Library', href: '/library', current: false },
{ label: data.ladderName, href: `/library/${params.ladder}`, current: true },
{ label: 'CPF Library', href: routes.library.index, current: false },
{ label: data.ladderName, href: `${routes.library.index}/${params.ladder}`, current: true },
]}
/>
{data && <LibraryDetailed data={data} />}
{data && <LibraryDetailed ladderSlug={params.ladder} data={data} />}
</div>
);
}

export const dynamic = 'force-dynamic';
29 changes: 12 additions & 17 deletions frontend/src/app/(app)/library/page.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,25 @@
import { mapKeysToCamelCase } from '@app/utils';
import { LadderCard, LadderCardInterface } from '@app/components/common/LadderCard';
import { API_URLS } from '@app/api';

async function getLadders() {
const response = await fetch(API_URLS.library.ladders);

if (!response.ok) {
throw new Error('Failed to fetch ladders');
}
const data = await response.json();

return mapKeysToCamelCase(data);
}
import { LadderCard } from '@app/components/common/LadderCard';
import { getLadders } from '@app/api/ladder';
import { Typography } from '@app/components/common/Typography';

export default async function LibraryPage() {
const data = await getLadders();

return (
<div>
<h1 className="text-lg mb-10 font-semibold leading-6 text-navy-900">CPF Library</h1>
<p className="mb-6 tracking-wide text-navy-600">Select a career path to view the details.</p>
<Typography className="mb-10" variant="body-l/semibold" as="h1">
CPF Library
</Typography>
<Typography className="mb-6 text-navy-600" variant="body-m/regular">
Select a career path to view the details.
</Typography>
<div className="grid grid-cols-3 gap-6">
{data.map((ladder: LadderCardInterface) => (
{data.map((ladder) => (
<LadderCard key={ladder.ladderSlug} {...ladder} />
))}
</div>
</div>
);
}

export const dynamic = 'force-dynamic';
11 changes: 11 additions & 0 deletions frontend/src/app/(app)/my-space/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Typography } from '@app/components/common/Typography';

export default function MySpace() {
return (
<div>
<Typography className="mb-10" variant="body-l/semibold" as="h1">
My Space
</Typography>
</div>
);
}
7 changes: 4 additions & 3 deletions frontend/src/app/(app)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { redirect } from 'next/navigation';
import { routes } from '@app/constants';

export default function Home() {
return (
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">We shall see</div>
);
redirect(routes.mySpace.index);
}
35 changes: 35 additions & 0 deletions frontend/src/app/(app)/people/my-profile/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Breadcrumbs } from '@app/components/modules/Breadcrumbs';
import { ProfileSettings } from '@app/components/modules/ProfileSetting';
import { routes } from '@app/constants';

// TODO: get data from api
const data = {
firstName: 'Jane',
lastName: 'Edge',
email: 'example@gmail.com',
ladders: [
{
ladderName: 'Front end',
technology: 'React',
band: 2,
},
],
notifications: {
slack: false,
email: false,
},
};

export default async function LibraryPage() {
return (
<div>
<Breadcrumbs
breadcrumbs={[
{ label: 'People', href: routes.people.index, current: false },
{ label: 'Profile setting', href: routes.people.myProfile, current: true },
]}
/>
<ProfileSettings data={data} />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface AccordionCardProps extends React.HTMLAttributes<HTMLDivElement> {
title: string;
expandedByDefault?: boolean;
small?: boolean;
}
Loading

0 comments on commit 5fec170

Please sign in to comment.