Skip to content

Commit 218417c

Browse files
committed
assignment v1
1 parent 171b0d6 commit 218417c

11 files changed

+4632
-1
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
.next

README.md

+57-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,57 @@
1-
# 前端面試題目
1+
# Ani Search (FE Coding Assignments)
2+
3+
## Expected Result Example
4+
5+
<img src="demo.gif" alt="demo">
6+
7+
## Features
8+
9+
- Search character by keyword, show character name and image from search result
10+
- Search staff by keyword, show staff name and photo from search result
11+
12+
Please implement above feature with
13+
14+
- React.js (Next.js) and Hook
15+
- Material-UI
16+
- GraphQL (Apollo GraphQL)
17+
18+
### Improvements
19+
20+
- No matched result handling
21+
- Error handling
22+
- and any improvements you want to do...
23+
24+
## Development
25+
26+
```bash
27+
$ yarn install --frozen-lockfile
28+
$ yarn dev
29+
```
30+
31+
## GraphQL API Document
32+
33+
- [anilist GraphiQL](https://anilist.co/graphiql)
34+
35+
- Character
36+
- Staff
37+
38+
- [API docs](https://anilist.gitbook.io/anilist-apiv2-docs/)
39+
40+
## Material-UI component you may want to use
41+
42+
- Box
43+
- Button
44+
- CircularProgress
45+
- Container
46+
- FormControl
47+
- InputLabel
48+
- MenuItem
49+
- Select
50+
- TextField
51+
- and any components you want to use...
52+
53+
[Material-UI](https://material-ui.com/)
54+
55+
## Contact
56+
57+
If you have any problems, please contact hau.chen@mediatek.com or open an issue.

demo.gif

1.98 MB
Loading

lib/apolloClient.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useMemo } from "react";
2+
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
3+
4+
let apolloClient = null;
5+
6+
function createApolloClient() {
7+
return new ApolloClient({
8+
ssrMode: false,
9+
link: new HttpLink({
10+
uri: "https://graphql.anilist.co",
11+
}),
12+
cache: new InMemoryCache(),
13+
});
14+
}
15+
16+
export function initializeApollo(initialState = null) {
17+
const _apolloClient = apolloClient ?? createApolloClient();
18+
19+
if (initialState) {
20+
const existingCache = _apolloClient.extract();
21+
22+
_apolloClient.cache.restore({ ...existingCache, ...initialState });
23+
}
24+
25+
if (typeof window === "undefined") return _apolloClient;
26+
27+
if (!apolloClient) apolloClient = _apolloClient;
28+
return _apolloClient;
29+
}
30+
31+
export function useApollo(initialState) {
32+
const store = useMemo(() => initializeApollo(initialState), [initialState]);
33+
return store;
34+
}

next-env.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/types/global" />

package.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"scripts": {
3+
"dev": "next dev",
4+
"build": "next build",
5+
"start": "next start"
6+
},
7+
"dependencies": {
8+
"@apollo/client": "^3.3.6",
9+
"@material-ui/core": "^4.11.2",
10+
"graphql": "^15.4.0",
11+
"next": "^10.0.5",
12+
"react": "^17.0.1",
13+
"react-dom": "^17.0.1"
14+
},
15+
"devDependencies": {
16+
"@types/node": "^14.14.20",
17+
"@types/react": "^17.0.0",
18+
"typescript": "^4.1.3"
19+
}
20+
}

pages/_app.tsx

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from "react";
2+
import PropTypes from "prop-types";
3+
import Head from "next/head";
4+
import { createMuiTheme } from "@material-ui/core";
5+
import { ThemeProvider } from "@material-ui/core/styles";
6+
import CssBaseline from "@material-ui/core/CssBaseline";
7+
import { ApolloProvider } from "@apollo/client";
8+
import { useApollo } from "../lib/apolloClient";
9+
10+
export default function MyApp(props) {
11+
const { Component, pageProps } = props;
12+
const theme = createMuiTheme();
13+
const apolloClient = useApollo(pageProps.initialApolloState);
14+
15+
React.useEffect(() => {
16+
// Remove the server-side injected CSS.
17+
const jssStyles = document.querySelector("#jss-server-side");
18+
if (jssStyles) {
19+
jssStyles.parentElement.removeChild(jssStyles);
20+
}
21+
}, []);
22+
23+
return (
24+
<React.Fragment>
25+
<Head>
26+
<title>Ani Search</title>
27+
<meta
28+
name="viewport"
29+
content="minimum-scale=1, initial-scale=1, width=device-width"
30+
/>
31+
</Head>
32+
<ThemeProvider theme={theme}>
33+
<ApolloProvider client={apolloClient}>
34+
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
35+
<CssBaseline />
36+
<Component {...pageProps} />
37+
</ApolloProvider>
38+
</ThemeProvider>
39+
</React.Fragment>
40+
);
41+
}
42+
43+
MyApp.propTypes = {
44+
Component: PropTypes.elementType.isRequired,
45+
pageProps: PropTypes.object.isRequired,
46+
};

pages/_document.tsx

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from "react";
2+
import Document, { Html, Head, Main, NextScript } from "next/document";
3+
import { ServerStyleSheets } from "@material-ui/core/styles";
4+
5+
export default class MyDocument extends Document {
6+
render() {
7+
return (
8+
<Html lang="en">
9+
<Head>
10+
<meta charSet="utf-8" />
11+
</Head>
12+
<body>
13+
<Main />
14+
<NextScript />
15+
</body>
16+
</Html>
17+
);
18+
}
19+
}
20+
21+
MyDocument.getInitialProps = async (ctx) => {
22+
const sheets = new ServerStyleSheets();
23+
const originalRenderPage = ctx.renderPage;
24+
25+
ctx.renderPage = () =>
26+
originalRenderPage({
27+
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
28+
});
29+
30+
const initialProps = await Document.getInitialProps(ctx);
31+
32+
return {
33+
...initialProps,
34+
// Styles fragment is rendered after the app and page rendering finish.
35+
styles: [
36+
...React.Children.toArray(initialProps.styles),
37+
sheets.getStyleElement(),
38+
],
39+
};
40+
};

pages/index.tsx

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as React from "react";
2+
import { Container } from "@material-ui/core";
3+
import { makeStyles } from "@material-ui/core/styles";
4+
5+
import { gql, useLazyQuery } from "@apollo/client";
6+
7+
export const CHARACTER_QUERY = gql`
8+
query queryCharacter($search: String!) {
9+
name {
10+
full
11+
native
12+
}
13+
image {
14+
medium
15+
}
16+
}
17+
`;
18+
19+
export const STAFF_QUERY = gql`
20+
query queryStaff($search: String!) {
21+
name {
22+
full
23+
native
24+
}
25+
image {
26+
medium
27+
}
28+
}
29+
`;
30+
31+
const useStyles = makeStyles((theme) => ({
32+
// TODO
33+
}));
34+
35+
function AniSearch() {
36+
const classes = useStyles();
37+
38+
const [getCharacter, characterResult] = useLazyQuery(CHARACTER_QUERY);
39+
const [getStaff, staffResult] = useLazyQuery(STAFF_QUERY);
40+
41+
// TODO
42+
43+
return <Container>{/* TODO */}</Container>;
44+
}
45+
46+
export default AniSearch;

tsconfig.json

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es5",
4+
"lib": [
5+
"dom",
6+
"dom.iterable",
7+
"esnext"
8+
],
9+
"allowJs": true,
10+
"skipLibCheck": true,
11+
"strict": false,
12+
"forceConsistentCasingInFileNames": true,
13+
"noEmit": true,
14+
"esModuleInterop": true,
15+
"module": "esnext",
16+
"moduleResolution": "node",
17+
"resolveJsonModule": true,
18+
"isolatedModules": true,
19+
"jsx": "preserve"
20+
},
21+
"include": [
22+
"next-env.d.ts",
23+
"**/*.ts",
24+
"**/*.tsx"
25+
],
26+
"exclude": [
27+
"node_modules"
28+
]
29+
}

0 commit comments

Comments
 (0)