Skip to content

Commit

Permalink
assignment v1
Browse files Browse the repository at this point in the history
  • Loading branch information
cettoana committed Jan 8, 2021
1 parent 171b0d6 commit 7d3d9cc
Show file tree
Hide file tree
Showing 11 changed files with 4,617 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.next
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,57 @@
# 前端面試題目
# Ani Search (FE Coding Assignments)

## Expected Result Example

<img src="demo.gif" alt="demo">

## Features

- Search character by keyword, show character name and image from search result
- Search staff by keyword, show staff name and photo from search result

Please implement above feature with

- React.js (Next.js) and Hook
- Material-UI
- GraphQL (Apollo GraphQL)

### Improvements

- No matched result handling
- Error handling
- and any improvements you want to do...

## Development

```bash
$ yarn install --frozen-lockfile
$ yarn dev
```

## GraphQL API Document

- [anilist GraphiQL](https://anilist.co/graphiql)

- Character
- Staff

- [API docs](https://anilist.gitbook.io/anilist-apiv2-docs/)

## Material-UI component you may want to use

- Box
- Button
- CircularProgress
- Container
- FormControl
- InputLabel
- MenuItem
- Select
- TextField
- and any components you want to use...

[Material-UI](https://material-ui.com/)

## Contact

If you have any problems, please contact [email protected] or open an issue.
Binary file added demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions lib/apolloClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useMemo } from "react";
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";

let apolloClient = null;

function createApolloClient() {
return new ApolloClient({
ssrMode: false,
link: new HttpLink({
uri: "https://graphql.anilist.co",
}),
cache: new InMemoryCache(),
});
}

export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient();

if (initialState) {
const existingCache = _apolloClient.extract();

_apolloClient.cache.restore({ ...existingCache, ...initialState });
}

if (typeof window === "undefined") return _apolloClient;

if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
}

export function useApollo(initialState) {
const store = useMemo(() => initializeApollo(initialState), [initialState]);
return store;
}
2 changes: 2 additions & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
20 changes: 20 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@apollo/client": "^3.3.6",
"@material-ui/core": "^4.11.2",
"graphql": "^15.4.0",
"next": "^10.0.5",
"react": "^17.0.1",
"react-dom": "^17.0.1"
},
"devDependencies": {
"@types/node": "^14.14.20",
"@types/react": "^17.0.0",
"typescript": "^4.1.3"
}
}
46 changes: 46 additions & 0 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";
import PropTypes from "prop-types";
import Head from "next/head";
import { createMuiTheme } from "@material-ui/core";
import { ThemeProvider } from "@material-ui/core/styles";
import CssBaseline from "@material-ui/core/CssBaseline";
import { ApolloProvider } from "@apollo/client";
import { useApollo } from "../lib/apolloClient";

export default function MyApp(props) {
const { Component, pageProps } = props;
const theme = createMuiTheme();
const apolloClient = useApollo(pageProps.initialApolloState);

React.useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);

return (
<React.Fragment>
<Head>
<title>Ani Search</title>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
</Head>
<ThemeProvider theme={theme}>
<ApolloProvider client={apolloClient}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Component {...pageProps} />
</ApolloProvider>
</ThemeProvider>
</React.Fragment>
);
}

MyApp.propTypes = {
Component: PropTypes.elementType.isRequired,
pageProps: PropTypes.object.isRequired,
};
40 changes: 40 additions & 0 deletions pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheets } from "@material-ui/core/styles";

export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
<meta charSet="utf-8" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}

MyDocument.getInitialProps = async (ctx) => {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;

ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});

const initialProps = await Document.getInitialProps(ctx);

return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
31 changes: 31 additions & 0 deletions pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as React from "react";
import { Container } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";

import { gql, useLazyQuery } from "@apollo/client";

export const CHARACTER_QUERY = gql`
query queryCharacter($search: String!) {
# TODO
}
`;

export const STAFF_QUERY = gql`
query queryStaff($search: String!) {
# TODO
}
`;

const useStyles = makeStyles((theme) => ({
// TODO
}));

function AniSearch() {
const classes = useStyles();

// TODO

return <Container>{/* TODO */}</Container>;
}

export default AniSearch;
29 changes: 29 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}
Loading

0 comments on commit 7d3d9cc

Please sign in to comment.