Compare commits

...

40 Commits

Author SHA1 Message Date
Dawid Wysokiński 30ca5b7f84
add .drone.yml
continuous-integration/drone/tag Build is passing Details
2022-09-25 13:15:48 +02:00
Dawid Wysokiński c5b82dcef4
refactor 2022-09-25 12:55:36 +02:00
renovate[bot] 84430bc856
chore(deps): update node.js to v14.19.1 (#74)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-18 21:28:19 +01:00
renovate[bot] d906767b6d
Update dependency @sentry/nextjs to v6.18.2 (#73)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-12 07:03:25 +00:00
renovate[bot] c5f49b4cc6
Update actions/checkout action to v3 (#72)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-12 05:25:22 +00:00
renovate[bot] 9832911eda
Update dependency @sentry/nextjs to v6.18.1 (#71)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-02-28 19:45:36 +00:00
renovate[bot] d3c808557e
Update dependency @sentry/nextjs to v6.18.0 (#66)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-02-24 20:57:59 +00:00
renovate[bot] d1230934db
Update Node.js to v14.19.0 (#65)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-02-18 06:25:33 +00:00
Dawid Wysokiński 4cd92ae646
chore: update packages (#67) 2022-02-18 06:24:34 +00:00
Dawid Wysokiński 8da1664a7f
Merge pull request #63 from zdam-egzamin-zawodowy/dependabot/npm_and_yarn/next-12.1.0
Bump next from 12.0.7 to 12.1.0
2022-02-18 06:05:26 +00:00
dependabot[bot] 53c28364bf
Bump next from 12.0.7 to 12.1.0
Bumps [next](https://github.com/vercel/next.js) from 12.0.7 to 12.1.0.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v12.0.7...v12.1.0)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-17 22:00:29 +00:00
Dawid Wysokiński aaa4687f34
Merge pull request #59 from zdam-egzamin-zawodowy/renovate/node-14.x
Update Node.js to v14.18.3
2022-01-12 06:43:40 +01:00
Renovate Bot 6449cf66e5
Update Node.js to v14.18.3 2022-01-11 21:36:10 +00:00
Dawid Wysokiński d58afe3d3e
Merge pull request #58 from zdam-egzamin-zawodowy/renovate/sentry-javascript-monorepo
Update dependency @sentry/nextjs to v6.16.1
2021-12-11 12:00:14 +01:00
Renovate Bot 4c19150e9a
Update dependency @sentry/nextjs to v6.16.1 2021-12-10 15:50:46 +00:00
Dawid Wysokiński f2020bac72
Merge pull request #57 from zdam-egzamin-zawodowy/renovate/nextjs-monorepo
Update dependency next to v12.0.7
2021-12-05 07:46:43 +01:00
Renovate Bot 69476fb8ea
Update dependency next to v12.0.7 2021-12-05 04:07:41 +00:00
Dawid Wysokiński e5b8f7cdde
Merge pull request #56 from zdam-egzamin-zawodowy/renovate/nextjs-monorepo
Update dependency next to v12.0.6
2021-12-04 17:24:05 +01:00
Renovate Bot e614cb08e9
Update dependency next to v12.0.6 2021-12-04 15:15:31 +00:00
Dawid Wysokiński 294ac3c50c
Merge pull request #55 from zdam-egzamin-zawodowy/renovate/node-14.x
Update Node.js to v14.18.2
2021-12-02 06:49:23 +01:00
Renovate Bot d09b67fe84
Update Node.js to v14.18.2 2021-12-02 02:30:22 +00:00
Dawid Wysokiński 08e93cd03e
Merge pull request #54 from zdam-egzamin-zawodowy/renovate/sentry-javascript-monorepo
Update dependency @sentry/nextjs to v6.15.0
2021-11-16 12:24:29 +01:00
Renovate Bot 6062480c34
Update dependency @sentry/nextjs to v6.15.0 2021-11-16 10:32:19 +00:00
Dawid Wysokiński 10e50474bc
Merge pull request #53 from zdam-egzamin-zawodowy/renovate/nextjs-monorepo
Update dependency next to v12.0.4
2021-11-16 07:42:23 +01:00
Renovate Bot 8c93025d48
Update dependency next to v12.0.4 2021-11-15 21:50:48 +00:00
Dawid Wysokiński e4c023ee14
Merge pull request #52 from zdam-egzamin-zawodowy/renovate/sentry-javascript-monorepo
Update dependency @sentry/nextjs to v6.14.3
2021-11-11 20:28:15 +01:00
Renovate Bot d30f4d434f
Update dependency @sentry/nextjs to v6.14.3 2021-11-11 18:58:14 +00:00
Dawid Wysokiński 88a31947d6
Merge pull request #51 from zdam-egzamin-zawodowy/renovate/sentry-javascript-monorepo
Update dependency @sentry/nextjs to v6.14.2
2021-11-11 17:12:04 +01:00
Renovate Bot 3107cac047
Update dependency @sentry/nextjs to v6.14.2 2021-11-11 15:49:32 +00:00
Dawid Wysokiński dc17be8ad1
sentry: add a prefix to the release name 2021-11-10 08:24:21 +01:00
Dawid Wysokiński 13a47d079f
fix: unnecessary spaces in Dockerfile 2021-11-07 08:11:05 +01:00
Dawid Wysokiński 642b7c83c7
Merge pull request #50 from zdam-egzamin-zawodowy/plausible
add and setup plausible
2021-11-07 08:06:32 +01:00
Dawid Wysokiński c515c7c4cf
remove a few unnecessary Sentry.captureExceptions 2021-11-07 08:05:43 +01:00
Dawid Wysokiński 0189f99c36
add and setup Plausible 2021-11-07 08:03:21 +01:00
Dawid Wysokiński 84f0a358df
update Sentry config 2021-11-06 10:42:02 +01:00
Dawid Wysokiński 00db95efd8
add Sentry.captureException to catch blocks 2021-11-06 08:36:26 +01:00
Dawid Wysokiński 7b6775c0fd
Merge pull request #48 from zdam-egzamin-zawodowy/renovate/sentry-javascript-monorepo
Update dependency @sentry/nextjs to v6.14.1
2021-11-06 08:18:23 +01:00
Dawid Wysokiński a47e07515f
Merge pull request #49 from zdam-egzamin-zawodowy/renovate/nextjs-monorepo
Update dependency next to v12.0.3
2021-11-06 08:16:32 +01:00
Renovate Bot 3b69a9ea1f
Update dependency next to v12.0.3 2021-11-05 20:09:34 +00:00
Renovate Bot b9b4709bf9
Update dependency @sentry/nextjs to v6.14.1 2021-11-05 15:32:30 +00:00
20 changed files with 1804 additions and 2630 deletions

59
.drone.yml Normal file
View File

@ -0,0 +1,59 @@
---
kind: pipeline
type: docker
name: linux-amd64
platform:
os: linux
arch: amd64
steps:
- name: publish
image: plugins/docker
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
registry: gitea.dwysokinski.me
repo: gitea.dwysokinski.me/zdam-egzamin-zawodowy-docker/website
auto_tag: true
auto_tag_suffix: linux-amd64
dockerfile: Dockerfile
trigger:
event:
- tag
---
kind: pipeline
type: docker
name: manifest
steps:
- name: manifest
image: plugins/manifest
settings:
auto_tag: "true"
ignore_missing: "true"
spec: manifest.tmpl
username:
from_secret: docker_username
password:
from_secret: docker_password
- name: manifest-latest
image: plugins/manifest
settings:
tags: latest
ignore_missing: "true"
spec: manifest.tmpl
username:
from_secret: docker_username
password:
from_secret: docker_password
trigger:
event:
- tag
depends_on:
- linux-amd64

3
.env
View File

@ -1,2 +1 @@
DATE_OF_THE_EXAM=2022-01-14T00:00:00.000Z
NEXT_PUBLIC_SENTRY_ENVIRONMENT=development
DATE_OF_THE_EXAM=2023-01-14T00:00:00.000Z

View File

@ -1,4 +1,3 @@
NEXT_PUBLIC_API_URI=https://api.zdamegzaminzawodowy.pl/graphql
NEXT_PUBLIC_URL=https://zdamegzaminzawodowy.pl
NEXT_PUBLIC_CDN_URI=https://cdn.zdamegzaminzawodowy.pl/
NEXT_PUBLIC_SENTRY_ENVIRONMENT=production

View File

@ -1,53 +0,0 @@
name: Build and push to registry
on:
push:
tags:
- '*'
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Get the version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.REGISTRY_LOGIN }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Push to Docker Hub
uses: docker/build-push-action@v2
with:
context: .
build-args: |
ENABLE_SENTRY_WEBPACK_PLUGIN=true
SENTRY_URL=${{ secrets.SENTRY_URL }}
SENTRY_ORG=${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT=${{ secrets.SENTRY_PROJECT }}
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN=${{ secrets.SENTRY_DSN }}
VERSION=v${{ steps.get_version.outputs.VERSION }}
tags: |
${{ secrets.REGISTRY_NAME }}/zdam-egzamin-zawodowy-website:latest
${{ secrets.REGISTRY_NAME }}/zdam-egzamin-zawodowy-website:${{ steps.get_version.outputs.VERSION }}
file: ./Dockerfile
push: true

View File

@ -1,5 +1,5 @@
# Install dependencies only when needed
FROM node:14.18.1-alpine AS deps
FROM node:14.19.1-alpine AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
@ -7,27 +7,11 @@ COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
# Rebuild the source code only when needed
FROM node:14.18.1-alpine AS builder
ARG VERSION="v0.0.0"
ARG ENABLE_SENTRY_WEBPACK_PLUGIN="false"
ARG SENTRY_URL=""
ARG SENTRY_ORG=""
ARG SENTRY_PROJECT=""
ARG SENTRY_AUTH_TOKEN=""
ARG SENTRY_DSN=""
FROM node:14.19.1-alpine AS builder
WORKDIR /app
ENV ENABLE_SENTRY_WEBPACK_PLUGIN=$ENABLE_SENTRY_WEBPACK_PLUGIN \
SENTRY_URL=$SENTRY_URL \
SENTRY_ORG=$SENTRY_ORG \
SENTRY_PROJECT=$SENTRY_PROJECT \
SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN \
SENTRY_DSN=$SENTRY_DSN \
NEXT_PUBLIC_SENTRY_DSN=$SENTRY_DSN \
NEXT_PUBLIC_VERSION=$VERSION \
BUILDING_PROCESS=true
ENV BUILDING_PROCESS=true
COPY . .
COPY --from=deps /app/node_modules ./node_modules
@ -35,19 +19,11 @@ COPY --from=deps /app/node_modules ./node_modules
RUN yarn build
# Production image, copy all the files and run next
FROM node:14.18.1-alpine AS runner
FROM node:14.19.1-alpine AS runner
WORKDIR /app
ARG VERSION="v0.0.0"
ARG SENTRY_DSN=""
ENV NODE_ENV=production \
SENTRY_DSN=$SENTRY_DSN \
NEXT_PUBLIC_SENTRY_DSN=$SENTRY_DSN \
VERSION=$VERSION \
NEXT_PUBLIC_VERSION=$VERSION \
BUILDING_PROCESS=false
ENV BUILDING_PROCESS=false
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public

View File

@ -1,29 +1,7 @@
# zdamegzaminzawodowy.pl
# [zdamegzaminzawodowy.pl](https://zdamegzaminzawodowy.pl)
![Screenshot](/screenshots/indexpage.png?raw=true)
A web app for practising the theoretical part of the [vocational exam](https://cke.gov.pl/en/vocational-examination/).
## Development
### Prerequisites
1. Node.JS
2. yarn
3. [Configured backend](https://github.com/zdam-egzamin-zawodowy/backend)
### Installation
1. Clone this repo
```
git clone git@github.com:zdam-egzamin-zawodowy/website.git
```
2. Open the folder with this project in a terminal.
3. ``yarn install``
4. ``yarn run dev``
## License
Distributed under the MIT License. See ``LICENSE`` for more information.

13
manifest.tmpl Normal file
View File

@ -0,0 +1,13 @@
image: gitea.dwysokinski.me/zdam-egzamin-zawodowy-docker/website:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
- {{this}}
{{/each}}
{{/if}}
manifests:
-
image: gitea.dwysokinski.me/zdam-egzamin-zawodowy-docker/website:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
platform:
architecture: amd64
os: linux

1
next-env.d.ts vendored
View File

@ -1,5 +1,4 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited

View File

@ -1,17 +1,8 @@
const { withSentryConfig } = require('@sentry/nextjs');
const CDN = process.env.NEXT_PUBLIC_CDN_URI
? new URL(process.env.NEXT_PUBLIC_CDN_URI)
: '';
const cfg = {
sentry: {
disableServerWebpackPlugin:
process.env.ENABLE_SENTRY_WEBPACK_PLUGIN !== 'true',
get disableClientWebpackPlugin() {
return this.disableServerWebpackPlugin;
},
},
module.exports = {
i18n: {
locales: ['pl'],
defaultLocale: 'pl',
@ -34,8 +25,3 @@ const cfg = {
];
},
};
module.exports = withSentryConfig(cfg, {
silent: true,
release: process.env.NEXT_PUBLIC_VERSION,
});

View File

@ -17,13 +17,12 @@
"@material-ui/core": "^4.11.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.57",
"@sentry/nextjs": "6.13.3",
"clsx": "^1.1.1",
"date-fns": "^2.19.0",
"graphql": "^15.5.0",
"graphql-request": "^3.4.0",
"lodash": "^4.17.21",
"next": "12.0.2",
"next": "12.1.0",
"next-sitemap": "^1.6.25",
"polish-plurals": "^1.1.0",
"react": "17.0.2",
@ -43,7 +42,7 @@
"babel-eslint": "^10.0.0",
"babel-plugin-transform-imports": "^2.0.0",
"eslint": "^7.5.0",
"eslint-config-next": "^12.0.0",
"eslint-config-next": "^12.1.0",
"eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-jsx-a11y": "^6.3.1",

View File

@ -1 +0,0 @@
import './sentry.server.config'

View File

@ -1,20 +0,0 @@
import * as Sentry from '@sentry/nextjs';
const initSentry = () => {
const SENTRY_DSN =
process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN;
if (!SENTRY_DSN) {
return;
}
Sentry.init({
dsn: SENTRY_DSN,
tracesSampleRate: 0.3,
environment:
process.env.SENTRY_ENVIRONMENT ||
process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT,
});
};
initSentry();

View File

@ -1,12 +1,10 @@
import { NextPage } from 'next';
import { ErrorProps } from 'next/error';
import * as Sentry from '@sentry/nextjs';
import { Box, Typography } from '@material-ui/core';
import Layout from 'common/Layout/Layout';
import Section from 'common/Section/Section';
import Seo from 'common/Seo/Seo';
import { useEffect } from 'react';
const getTitleForStatusCode = (statusCode: number): string => {
switch (statusCode) {
@ -17,20 +15,7 @@ const getTitleForStatusCode = (statusCode: number): string => {
}
};
const ErrorPage: NextPage<
ErrorProps & { hasGetInitialPropsRun?: boolean; err?: any }
> = ({ statusCode, title, hasGetInitialPropsRun, err }) => {
useEffect(() => {
// getInitialProps is not called in case of
// https://github.com/vercel/next.js/issues/8592. As a workaround, we pass
// err via _app.js so it can be captured
if (hasGetInitialPropsRun || !err) {
return;
}
Sentry.captureException(err);
}, [hasGetInitialPropsRun, err]);
const ErrorPage: NextPage<ErrorProps> = ({ statusCode, title }) => {
const _title = title ?? getTitleForStatusCode(statusCode);
return (
@ -54,26 +39,10 @@ const ErrorPage: NextPage<
);
};
ErrorPage.getInitialProps = async ({ res, err, asPath }) => {
const props = {
ErrorPage.getInitialProps = async ({ res, err }) => {
return {
statusCode: (res ? res.statusCode : err?.statusCode) ?? 404,
hasGetInitialPropsRun: true,
};
if (!err) {
Sentry.captureException(
new Error(`_error.js getInitialProps missing data at path: ${asPath}`)
);
await Sentry.flush(2000);
return props;
}
Sentry.captureException(err);
await Sentry.flush(2000);
return props;
};
export default ErrorPage;

View File

@ -17,7 +17,6 @@ import AboutExam from './components/AboutExam/AboutExam';
import ExamParts from './components/ExamParts/ExamParts';
import CheckMobileApp from './components/CheckMobileApp/CheckMobileApp';
import Professions from './components/Professions/Professions';
import * as Sentry from '@sentry/nextjs';
interface IndexPageProps {
professions: Profession[];

View File

@ -58,74 +58,76 @@ const SUGGESTIONS_LIMIT = 6;
const REVALIDATE_ERROR = 600;
const REVALIDATE_SUCCESS = 10;
export const getStaticProps: GetStaticProps<
TestPageProps,
TestPageParams
> = async ({ params }) => {
const props: TestPageProps = {
suggestions: [],
questions: [],
qualification: {
id: 0,
slug: '',
name: '',
code: '',
createdAt: new Date(0),
},
export const getStaticProps: GetStaticProps<TestPageProps, TestPageParams> =
async ({ params }) => {
const props: TestPageProps = {
suggestions: [],
questions: [],
qualification: {
id: 0,
slug: '',
name: '',
code: '',
createdAt: new Date(0),
},
};
if (!params || isNil(params.limit) || !isString(params.slug))
return { notFound: true, revalidate: REVALIDATE_ERROR };
const limit = parseInt(params.limit);
const slug = params.slug.trim();
if (
isNaN(limit) ||
!QUESTIONS.some(numOfQuestions => numOfQuestions === limit)
) {
return {
props,
redirect: {
destination: resolveAs({
pathname: Route.TestPage,
query: { ...params, limit: QUESTIONS[QUESTIONS.length - 1] },
}),
},
revalidate: REVALIDATE_ERROR,
};
}
const client = createClient();
try {
const { qualification } = await client.request<
Pick<Query, 'qualification'>,
QueryQualificationArgs
>(QUERY_QUALIFICATION, { slug });
if (!qualification) {
throw new Error('Qualification not found: slug=' + slug);
}
props.qualification = qualification;
const { generateTest, similarQualifications } = await client.request<
Pick<Query, 'generateTest' | 'similarQualifications'>,
QueryGenerateTestSimilarQualificationsArgs
>(QUERY_GENERATE_TEST_SIMILAR_QUALIFICATIONS, {
limitSuggestions: SUGGESTIONS_LIMIT,
qualificationID: qualification.id,
limitTest: limit,
skipSuggestions: false,
});
if (Array.isArray(generateTest)) {
props.questions = generateTest;
}
if (Array.isArray(similarQualifications.items)) {
props.suggestions = similarQualifications.items;
}
return {
props,
revalidate: REVALIDATE_SUCCESS,
};
} catch (e) {
return { notFound: true, revalidate: REVALIDATE_ERROR };
}
};
if (!params || isNil(params.limit) || !isString(params.slug))
return { notFound: true, revalidate: REVALIDATE_ERROR };
const limit = parseInt(params.limit);
const slug = params.slug.trim();
if (
isNaN(limit) ||
!QUESTIONS.some(numOfQuestions => numOfQuestions === limit)
) {
return {
props,
redirect: {
destination: resolveAs({
pathname: Route.TestPage,
query: { ...params, limit: QUESTIONS[QUESTIONS.length - 1] },
}),
},
revalidate: REVALIDATE_ERROR,
};
}
const client = createClient();
try {
const { qualification } = await client.request<
Pick<Query, 'qualification'>,
QueryQualificationArgs
>(QUERY_QUALIFICATION, { slug });
if (!qualification) {
throw new Error('404');
}
props.qualification = qualification;
const { generateTest, similarQualifications } = await client.request<
Pick<Query, 'generateTest' | 'similarQualifications'>,
QueryGenerateTestSimilarQualificationsArgs
>(QUERY_GENERATE_TEST_SIMILAR_QUALIFICATIONS, {
limitSuggestions: SUGGESTIONS_LIMIT,
qualificationID: qualification.id,
limitTest: limit,
skipSuggestions: false,
});
if (Array.isArray(generateTest)) {
props.questions = generateTest;
}
if (Array.isArray(similarQualifications.items)) {
props.suggestions = similarQualifications.items;
}
return {
props,
revalidate: REVALIDATE_SUCCESS,
};
} catch (e) {
return { notFound: true, revalidate: REVALIDATE_ERROR };
}
};
export default TestPage;

View File

@ -97,6 +97,7 @@ const Test = ({ initialQuestions, qualification }: TestProps) => {
try {
setIsFetching(true);
const { generateTest: newQuestions } = await createClient().request<
Pick<Query, 'generateTest'>,
QueryGenerateTestSimilarQualificationsArgs
@ -106,6 +107,7 @@ const Test = ({ initialQuestions, qualification }: TestProps) => {
skipSuggestions: true,
qualificationID: qualification.id,
});
resetValues(newQuestions);
} catch (e) {}

View File

@ -1,4 +1,5 @@
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
@ -13,9 +14,6 @@ export type Scalars = {
Upload: any;
};
export enum Answer {
A = 'a',
B = 'b',
@ -25,19 +23,19 @@ export enum Answer {
export type Mutation = {
createProfession?: Maybe<Profession>;
updateProfession?: Maybe<Profession>;
deleteProfessions?: Maybe<Array<Profession>>;
createQualification?: Maybe<Qualification>;
updateQualification?: Maybe<Qualification>;
deleteQualifications?: Maybe<Array<Qualification>>;
createQuestion?: Maybe<Question>;
updateQuestion?: Maybe<Question>;
deleteQuestions?: Maybe<Array<Question>>;
createUser?: Maybe<User>;
updateUser?: Maybe<User>;
updateManyUsers?: Maybe<Array<User>>;
deleteProfessions?: Maybe<Array<Profession>>;
deleteQualifications?: Maybe<Array<Qualification>>;
deleteQuestions?: Maybe<Array<Question>>;
deleteUsers?: Maybe<Array<User>>;
signIn?: Maybe<UserWithToken>;
updateManyUsers?: Maybe<Array<User>>;
updateProfession?: Maybe<Profession>;
updateQualification?: Maybe<Qualification>;
updateQuestion?: Maybe<Question>;
updateUser?: Maybe<User>;
};
@ -46,63 +44,33 @@ export type MutationCreateProfessionArgs = {
};
export type MutationUpdateProfessionArgs = {
id: Scalars['ID'];
input: ProfessionInput;
};
export type MutationDeleteProfessionsArgs = {
ids: Array<Scalars['ID']>;
};
export type MutationCreateQualificationArgs = {
input: QualificationInput;
};
export type MutationUpdateQualificationArgs = {
id: Scalars['ID'];
input: QualificationInput;
};
export type MutationDeleteQualificationsArgs = {
ids: Array<Scalars['ID']>;
};
export type MutationCreateQuestionArgs = {
input: QuestionInput;
};
export type MutationUpdateQuestionArgs = {
id: Scalars['ID'];
input: QuestionInput;
};
export type MutationDeleteQuestionsArgs = {
ids: Array<Scalars['ID']>;
};
export type MutationCreateUserArgs = {
input: UserInput;
};
export type MutationUpdateUserArgs = {
id: Scalars['ID'];
input: UserInput;
export type MutationDeleteProfessionsArgs = {
ids: Array<Scalars['ID']>;
};
export type MutationUpdateManyUsersArgs = {
export type MutationDeleteQualificationsArgs = {
ids: Array<Scalars['ID']>;
};
export type MutationDeleteQuestionsArgs = {
ids: Array<Scalars['ID']>;
input: UpdateManyUsersInput;
};
@ -114,173 +82,195 @@ export type MutationDeleteUsersArgs = {
export type MutationSignInArgs = {
email: Scalars['String'];
password: Scalars['String'];
staySignedIn?: Maybe<Scalars['Boolean']>;
staySignedIn?: InputMaybe<Scalars['Boolean']>;
};
export type MutationUpdateManyUsersArgs = {
ids: Array<Scalars['ID']>;
input: UpdateManyUsersInput;
};
export type MutationUpdateProfessionArgs = {
id: Scalars['ID'];
input: ProfessionInput;
};
export type MutationUpdateQualificationArgs = {
id: Scalars['ID'];
input: QualificationInput;
};
export type MutationUpdateQuestionArgs = {
id: Scalars['ID'];
input: QuestionInput;
};
export type MutationUpdateUserArgs = {
id: Scalars['ID'];
input: UserInput;
};
export type Profession = {
id: Scalars['ID'];
slug: Scalars['String'];
name: Scalars['String'];
description?: Maybe<Scalars['String']>;
createdAt: Scalars['Time'];
description?: Maybe<Scalars['String']>;
id: Scalars['ID'];
name: Scalars['String'];
qualifications: Array<Qualification>;
slug: Scalars['String'];
};
export type ProfessionFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
slug?: Maybe<Array<Scalars['String']>>;
slugNEQ?: Maybe<Array<Scalars['String']>>;
name?: Maybe<Array<Scalars['String']>>;
nameNEQ?: Maybe<Array<Scalars['String']>>;
nameIEQ?: Maybe<Scalars['String']>;
nameMATCH?: Maybe<Scalars['String']>;
descriptionIEQ?: Maybe<Scalars['String']>;
descriptionMATCH?: Maybe<Scalars['String']>;
qualificationID?: Maybe<Array<Scalars['ID']>>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
createdAt?: InputMaybe<Scalars['Time']>;
createdAtGT?: InputMaybe<Scalars['Time']>;
createdAtGTE?: InputMaybe<Scalars['Time']>;
createdAtLT?: InputMaybe<Scalars['Time']>;
createdAtLTE?: InputMaybe<Scalars['Time']>;
descriptionIEQ?: InputMaybe<Scalars['String']>;
descriptionMATCH?: InputMaybe<Scalars['String']>;
id?: InputMaybe<Array<Scalars['ID']>>;
idNEQ?: InputMaybe<Array<Scalars['ID']>>;
name?: InputMaybe<Array<Scalars['String']>>;
nameIEQ?: InputMaybe<Scalars['String']>;
nameMATCH?: InputMaybe<Scalars['String']>;
nameNEQ?: InputMaybe<Array<Scalars['String']>>;
qualificationID?: InputMaybe<Array<Scalars['ID']>>;
slug?: InputMaybe<Array<Scalars['String']>>;
slugNEQ?: InputMaybe<Array<Scalars['String']>>;
};
export type ProfessionInput = {
name?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
description?: InputMaybe<Scalars['String']>;
name?: InputMaybe<Scalars['String']>;
};
export type ProfessionList = {
total: Scalars['Int'];
items?: Maybe<Array<Profession>>;
total: Scalars['Int'];
};
export type Qualification = {
id: Scalars['ID'];
slug: Scalars['String'];
name: Scalars['String'];
code: Scalars['String'];
formula?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
createdAt: Scalars['Time'];
description?: Maybe<Scalars['String']>;
formula?: Maybe<Scalars['String']>;
id: Scalars['ID'];
name: Scalars['String'];
slug: Scalars['String'];
};
export type QualificationFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
slug?: Maybe<Array<Scalars['String']>>;
slugNEQ?: Maybe<Array<Scalars['String']>>;
formula?: Maybe<Array<Scalars['String']>>;
formulaNEQ?: Maybe<Array<Scalars['String']>>;
name?: Maybe<Array<Scalars['String']>>;
nameNEQ?: Maybe<Array<Scalars['String']>>;
nameIEQ?: Maybe<Scalars['String']>;
nameMATCH?: Maybe<Scalars['String']>;
code?: Maybe<Array<Scalars['String']>>;
codeNEQ?: Maybe<Array<Scalars['String']>>;
codeIEQ?: Maybe<Scalars['String']>;
codeMATCH?: Maybe<Scalars['String']>;
descriptionIEQ?: Maybe<Scalars['String']>;
descriptionMATCH?: Maybe<Scalars['String']>;
professionID?: Maybe<Array<Scalars['Int']>>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
or?: Maybe<QualificationFilterOr>;
code?: InputMaybe<Array<Scalars['String']>>;
codeIEQ?: InputMaybe<Scalars['String']>;
codeMATCH?: InputMaybe<Scalars['String']>;
codeNEQ?: InputMaybe<Array<Scalars['String']>>;
createdAt?: InputMaybe<Scalars['Time']>;
createdAtGT?: InputMaybe<Scalars['Time']>;
createdAtGTE?: InputMaybe<Scalars['Time']>;
createdAtLT?: InputMaybe<Scalars['Time']>;
createdAtLTE?: InputMaybe<Scalars['Time']>;
descriptionIEQ?: InputMaybe<Scalars['String']>;
descriptionMATCH?: InputMaybe<Scalars['String']>;
formula?: InputMaybe<Array<Scalars['String']>>;
formulaNEQ?: InputMaybe<Array<Scalars['String']>>;
id?: InputMaybe<Array<Scalars['ID']>>;
idNEQ?: InputMaybe<Array<Scalars['ID']>>;
name?: InputMaybe<Array<Scalars['String']>>;
nameIEQ?: InputMaybe<Scalars['String']>;
nameMATCH?: InputMaybe<Scalars['String']>;
nameNEQ?: InputMaybe<Array<Scalars['String']>>;
or?: InputMaybe<QualificationFilterOr>;
professionID?: InputMaybe<Array<Scalars['Int']>>;
slug?: InputMaybe<Array<Scalars['String']>>;
slugNEQ?: InputMaybe<Array<Scalars['String']>>;
};
export type QualificationFilterOr = {
nameMatch?: Maybe<Scalars['String']>;
nameIEQ?: Maybe<Scalars['String']>;
codeMatch?: Maybe<Scalars['String']>;
codeIEQ?: Maybe<Scalars['String']>;
codeIEQ?: InputMaybe<Scalars['String']>;
codeMatch?: InputMaybe<Scalars['String']>;
nameIEQ?: InputMaybe<Scalars['String']>;
nameMatch?: InputMaybe<Scalars['String']>;
};
export type QualificationInput = {
name?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
code?: Maybe<Scalars['String']>;
formula?: Maybe<Scalars['String']>;
associateProfession?: Maybe<Array<Scalars['Int']>>;
dissociateProfession?: Maybe<Array<Scalars['Int']>>;
associateProfession?: InputMaybe<Array<Scalars['Int']>>;
code?: InputMaybe<Scalars['String']>;
description?: InputMaybe<Scalars['String']>;
dissociateProfession?: InputMaybe<Array<Scalars['Int']>>;
formula?: InputMaybe<Scalars['String']>;
name?: InputMaybe<Scalars['String']>;
};
export type QualificationList = {
total: Scalars['Int'];
items?: Maybe<Array<Qualification>>;
total: Scalars['Int'];
};
export type Query = {
professions: ProfessionList;
profession?: Maybe<Profession>;
qualifications: QualificationList;
similarQualifications: QualificationList;
qualification?: Maybe<Qualification>;
questions: QuestionList;
generateTest?: Maybe<Array<Question>>;
users: UserList;
user?: Maybe<User>;
me?: Maybe<User>;
};
export type QueryProfessionsArgs = {
filter?: Maybe<ProfessionFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QueryProfessionArgs = {
id?: Maybe<Scalars['ID']>;
slug?: Maybe<Scalars['String']>;
};
export type QueryQualificationsArgs = {
filter?: Maybe<QualificationFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QuerySimilarQualificationsArgs = {
qualificationID: Scalars['ID'];
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
};
export type QueryQualificationArgs = {
id?: Maybe<Scalars['ID']>;
slug?: Maybe<Scalars['String']>;
};
export type QueryQuestionsArgs = {
filter?: Maybe<QuestionFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
profession?: Maybe<Profession>;
professions: ProfessionList;
qualification?: Maybe<Qualification>;
qualifications: QualificationList;
questions: QuestionList;
similarQualifications: QualificationList;
user?: Maybe<User>;
users: UserList;
};
export type QueryGenerateTestArgs = {
limit?: InputMaybe<Scalars['Int']>;
qualificationIDs: Array<Scalars['ID']>;
limit?: Maybe<Scalars['Int']>;
};
export type QueryUsersArgs = {
filter?: Maybe<UserFilter>;
limit?: Maybe<Scalars['Int']>;
offset?: Maybe<Scalars['Int']>;
sort?: Maybe<Array<Scalars['String']>>;
export type QueryProfessionArgs = {
id?: InputMaybe<Scalars['ID']>;
slug?: InputMaybe<Scalars['String']>;
};
export type QueryProfessionsArgs = {
filter?: InputMaybe<ProfessionFilter>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
sort?: InputMaybe<Array<Scalars['String']>>;
};
export type QueryQualificationArgs = {
id?: InputMaybe<Scalars['ID']>;
slug?: InputMaybe<Scalars['String']>;
};
export type QueryQualificationsArgs = {
filter?: InputMaybe<QualificationFilter>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
sort?: InputMaybe<Array<Scalars['String']>>;
};
export type QueryQuestionsArgs = {
filter?: InputMaybe<QuestionFilter>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
sort?: InputMaybe<Array<Scalars['String']>>;
};
export type QuerySimilarQualificationsArgs = {
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
qualificationID: Scalars['ID'];
sort?: InputMaybe<Array<Scalars['String']>>;
};
@ -288,13 +278,15 @@ export type QueryUserArgs = {
id: Scalars['ID'];
};
export type QueryUsersArgs = {
filter?: InputMaybe<UserFilter>;
limit?: InputMaybe<Scalars['Int']>;
offset?: InputMaybe<Scalars['Int']>;
sort?: InputMaybe<Array<Scalars['String']>>;
};
export type Question = {
id: Scalars['ID'];
from?: Maybe<Scalars['String']>;
content: Scalars['String'];
explanation?: Maybe<Scalars['String']>;
correctAnswer: Answer;
image?: Maybe<Scalars['String']>;
answerA?: Maybe<Scalars['String']>;
answerAImage?: Maybe<Scalars['String']>;
answerB?: Maybe<Scalars['String']>;
@ -303,52 +295,58 @@ export type Question = {
answerCImage?: Maybe<Scalars['String']>;
answerD?: Maybe<Scalars['String']>;
answerDImage?: Maybe<Scalars['String']>;
qualification?: Maybe<Qualification>;
content: Scalars['String'];
correctAnswer: Answer;
createdAt: Scalars['Time'];
explanation?: Maybe<Scalars['String']>;
from?: Maybe<Scalars['String']>;
id: Scalars['ID'];
image?: Maybe<Scalars['String']>;
qualification?: Maybe<Qualification>;
updatedAt: Scalars['Time'];
};
export type QuestionFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
from?: Maybe<Array<Scalars['String']>>;
contentIEQ?: Maybe<Scalars['String']>;
contentMATCH?: Maybe<Scalars['String']>;
qualificationID?: Maybe<Array<Scalars['Int']>>;
qualificationIDNEQ?: Maybe<Array<Scalars['Int']>>;
qualificationFilter?: Maybe<QualificationFilter>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
contentIEQ?: InputMaybe<Scalars['String']>;
contentMATCH?: InputMaybe<Scalars['String']>;
createdAt?: InputMaybe<Scalars['Time']>;
createdAtGT?: InputMaybe<Scalars['Time']>;
createdAtGTE?: InputMaybe<Scalars['Time']>;
createdAtLT?: InputMaybe<Scalars['Time']>;
createdAtLTE?: InputMaybe<Scalars['Time']>;
from?: InputMaybe<Array<Scalars['String']>>;
id?: InputMaybe<Array<Scalars['ID']>>;
idNEQ?: InputMaybe<Array<Scalars['ID']>>;
qualificationFilter?: InputMaybe<QualificationFilter>;
qualificationID?: InputMaybe<Array<Scalars['Int']>>;
qualificationIDNEQ?: InputMaybe<Array<Scalars['Int']>>;
};
export type QuestionInput = {
content?: Maybe<Scalars['String']>;
from?: Maybe<Scalars['String']>;
explanation?: Maybe<Scalars['String']>;
correctAnswer?: Maybe<Answer>;
qualificationID?: Maybe<Scalars['Int']>;
image?: Maybe<Scalars['Upload']>;
deleteImage?: Maybe<Scalars['Boolean']>;
answerA?: Maybe<Scalars['String']>;
answerAImage?: Maybe<Scalars['Upload']>;
deleteAnswerAImage?: Maybe<Scalars['Boolean']>;
answerB?: Maybe<Scalars['String']>;
answerBImage?: Maybe<Scalars['Upload']>;
deleteAnswerBImage?: Maybe<Scalars['Boolean']>;
answerC?: Maybe<Scalars['String']>;
answerCImage?: Maybe<Scalars['Upload']>;
deleteAnswerCImage?: Maybe<Scalars['Boolean']>;
answerD?: Maybe<Scalars['String']>;
answerDImage?: Maybe<Scalars['Upload']>;
deleteAnswerDImage?: Maybe<Scalars['Boolean']>;
answerA?: InputMaybe<Scalars['String']>;
answerAImage?: InputMaybe<Scalars['Upload']>;
answerB?: InputMaybe<Scalars['String']>;
answerBImage?: InputMaybe<Scalars['Upload']>;
answerC?: InputMaybe<Scalars['String']>;
answerCImage?: InputMaybe<Scalars['Upload']>;
answerD?: InputMaybe<Scalars['String']>;
answerDImage?: InputMaybe<Scalars['Upload']>;
content?: InputMaybe<Scalars['String']>;
correctAnswer?: InputMaybe<Answer>;
deleteAnswerAImage?: InputMaybe<Scalars['Boolean']>;
deleteAnswerBImage?: InputMaybe<Scalars['Boolean']>;
deleteAnswerCImage?: InputMaybe<Scalars['Boolean']>;
deleteAnswerDImage?: InputMaybe<Scalars['Boolean']>;
deleteImage?: InputMaybe<Scalars['Boolean']>;
explanation?: InputMaybe<Scalars['String']>;
from?: InputMaybe<Scalars['String']>;
image?: InputMaybe<Scalars['Upload']>;
qualificationID?: InputMaybe<Scalars['Int']>;
};
export type QuestionList = {
total: Scalars['Int'];
items?: Maybe<Array<Question>>;
total: Scalars['Int'];
};
export enum Role {
@ -356,62 +354,60 @@ export enum Role {
User = 'user'
}
export type UpdateManyUsersInput = {
role?: Maybe<Role>;
activated?: Maybe<Scalars['Boolean']>;
activated?: InputMaybe<Scalars['Boolean']>;
role?: InputMaybe<Role>;
};
export type User = {
id: Scalars['ID'];
displayName: Scalars['String'];
role: Role;
email: Scalars['String'];
activated: Scalars['Boolean'];
createdAt: Scalars['Time'];
displayName: Scalars['String'];
email: Scalars['String'];
id: Scalars['ID'];
role: Role;
};
export type UserFilter = {
id?: Maybe<Array<Scalars['ID']>>;
idNEQ?: Maybe<Array<Scalars['ID']>>;
activated?: Maybe<Scalars['Boolean']>;
displayName?: Maybe<Array<Scalars['String']>>;
displayNameNEQ?: Maybe<Array<Scalars['String']>>;
displayNameIEQ?: Maybe<Scalars['String']>;
displayNameMATCH?: Maybe<Scalars['String']>;
email?: Maybe<Array<Scalars['String']>>;
emailNEQ?: Maybe<Array<Scalars['String']>>;
emailIEQ?: Maybe<Scalars['String']>;
emailMATCH?: Maybe<Scalars['String']>;
role?: Maybe<Array<Role>>;
roleNEQ?: Maybe<Array<Role>>;
createdAt?: Maybe<Scalars['Time']>;
createdAtGT?: Maybe<Scalars['Time']>;
createdAtGTE?: Maybe<Scalars['Time']>;
createdAtLT?: Maybe<Scalars['Time']>;
createdAtLTE?: Maybe<Scalars['Time']>;
or?: Maybe<UserFilterOr>;
activated?: InputMaybe<Scalars['Boolean']>;
createdAt?: InputMaybe<Scalars['Time']>;
createdAtGT?: InputMaybe<Scalars['Time']>;
createdAtGTE?: InputMaybe<Scalars['Time']>;
createdAtLT?: InputMaybe<Scalars['Time']>;
createdAtLTE?: InputMaybe<Scalars['Time']>;
displayName?: InputMaybe<Array<Scalars['String']>>;
displayNameIEQ?: InputMaybe<Scalars['String']>;
displayNameMATCH?: InputMaybe<Scalars['String']>;
displayNameNEQ?: InputMaybe<Array<Scalars['String']>>;
email?: InputMaybe<Array<Scalars['String']>>;
emailIEQ?: InputMaybe<Scalars['String']>;
emailMATCH?: InputMaybe<Scalars['String']>;
emailNEQ?: InputMaybe<Array<Scalars['String']>>;
id?: InputMaybe<Array<Scalars['ID']>>;
idNEQ?: InputMaybe<Array<Scalars['ID']>>;
or?: InputMaybe<UserFilterOr>;
role?: InputMaybe<Array<Role>>;
roleNEQ?: InputMaybe<Array<Role>>;
};
export type UserFilterOr = {
displayNameIEQ?: Maybe<Scalars['String']>;
displayNameMATCH?: Maybe<Scalars['String']>;
emailIEQ?: Maybe<Scalars['String']>;
emailMATCH?: Maybe<Scalars['String']>;
displayNameIEQ?: InputMaybe<Scalars['String']>;
displayNameMATCH?: InputMaybe<Scalars['String']>;
emailIEQ?: InputMaybe<Scalars['String']>;
emailMATCH?: InputMaybe<Scalars['String']>;
};
export type UserInput = {
displayName?: Maybe<Scalars['String']>;
password?: Maybe<Scalars['String']>;
email?: Maybe<Scalars['String']>;
role?: Maybe<Role>;
activated?: Maybe<Scalars['Boolean']>;
activated?: InputMaybe<Scalars['Boolean']>;
displayName?: InputMaybe<Scalars['String']>;
email?: InputMaybe<Scalars['String']>;
password?: InputMaybe<Scalars['String']>;
role?: InputMaybe<Role>;
};
export type UserList = {
total: Scalars['Int'];
items?: Maybe<Array<User>>;
total: Scalars['Int'];
};
export type UserWithToken = {

View File

@ -3,7 +3,7 @@ import { useEffect } from 'react';
import { AppProps } from 'next/app';
import ThemeProvider from 'libs/material-ui/ThemeProvider';
function MyApp({ Component, pageProps }: AppProps) {
const App = ({ Component, pageProps }: AppProps) => {
useEffect(() => {
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles && jssStyles.parentElement) {
@ -16,6 +16,6 @@ function MyApp({ Component, pageProps }: AppProps) {
<Component {...pageProps} />
</ThemeProvider>
);
}
};
export default MyApp;
export default App;

View File

@ -1,7 +1,6 @@
import { GetServerSideProps } from 'next';
import { gql } from 'graphql-request';
import { getServerSideSitemap } from 'next-sitemap';
import * as Sentry from '@sentry/nextjs';
import { ISitemapField } from 'next-sitemap/dist/@types/interface';
import { createClient } from 'libs/graphql';
import { Query, QueryQualificationsArgs } from 'libs/graphql';
@ -49,7 +48,6 @@ export const getServerSideProps: GetServerSideProps = async ctx => {
}
} catch (e) {
console.log('server-sitemap.xml', e.message);
Sentry.captureException(e);
}
return getServerSideSitemap(ctx, fields);

3528
yarn.lock

File diff suppressed because it is too large Load Diff