이어서 할 곳

GitHub - fc-micro-frontends/career-up at step12

REACT_APP_AUTH0_DOMAIN=dev-vcrmf0xuep020tri.us.auth0.com
REACT_APP_AUTH0_CLIENT_ID=27LLb4I9cIjiiQNSjbNIX9sQM1KECUfk
REACT_APP_AUTH0_CALLBACK_URL=http://localhost:3000
➜ pnpm i

➜ pnpm build

API 코드 작성

// career-up/apps/network/src/types.ts

import { type User } from "@auth0/auth0-spa-js";

export interface UserType extends User {}

export interface MyNetworkType {
  connectionCount: number;
  contactCount: number;
  eventCount: number;
  pageCount: number;

  user: UserType;
}

export interface ConnectionType {
  name: string;
  picture: string | null;
  role: string;
  networkCount: number;
}
// career-up/apps/network/src/apis.ts

import { type ConnectionType, type MyNetworkType } from "./types";

export async function getMyNetwork(token: string): Promise<MyNetworkType> {
  const res = await fetch("<http://localhost:4000/my-network>", {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

  return await res.json();
}

export async function getConnections(token: string): Promise<ConnectionType[]> {
  const res = await fetch("<http://localhost:4000/connections>", {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

  return await res.json();
}

공통 Layout 처리

// career-up/apps/network/src/routes.tsx

import React from "react";
import { type RouteObject } from "react-router-dom";
import { AppRoutingManager } from "@career-up/shell-router";
import Auth0ClientProvider from "./providers/auth0-client-provider";
import { RecoilRoot } from "recoil";
**import Layout from "./components/layout";**

export const routes: RouteObject[] = [
  {
    path: "/",
    element: (
      <RecoilRoot>
        <Auth0ClientProvider>
          **<Layout>**
            <AppRoutingManager type="app-network" />
          **</Layout>**
        </Auth0ClientProvider>
      </RecoilRoot>
    ),
    errorElement: <div>App Network Error</div>,
    children: [
      {
        index: true,
        element: <div className="network--text-9xl">network home</div>,
      },
    ],
  },
];
// career-up/apps/network/src/styles/f.css.ts

const prefix = "network--";

export const style = (classNames: string[]): string => {
  return classNames.map((className) => `${prefix}${className}`).join(" ");
};
// career-up/apps/network/src/index.scss

@tailwind base;
@tailwind components;
@tailwind utilities;

html {
  line-height: normal;
}

body {
  font-family: Arial, Helvetica, sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f4f2ee;
}

img {
  max-width: none;
}

button {
  font-size: revert;
}

* {
  box-sizing: revert;
}

h3,
p {
  all: revert;
}

**.network--mx-w-1128px {
  max-width: 1128px;
}

.network--w-225px {
  width: 225px;
}

.network--w-879px {
  width: 879px;
}**
// career-up/apps/network/src/components/layout.css.ts

import { style } from "../styles/f.css";

export const wrapper = style([
  "flex",
  "flex-row",
  "gap-6",
  "mx-auto",
  "my-0",
  "mx-w-1128px",
  "p-4",
]);

export const left = style(["flex", "flex-col", "w-225px", "gap-2.5"]);

export const center = style(["flex", "flex-col", "w-879px", "gap-2.5"]);
// career-up/apps/network/src/components/layout.tsx

import React from "react";
import { wrapper, center, left } from "./layout.css";
import MyNetworkContainer from "../containers/my-network-container";

const Layout: React.FC<React.PropsWithChildren> = ({ children }) => {
  return (
    <div className={wrapper}>
      <div className={left}>
        <MyNetworkContainer />
      </div>
      <div className={center}>{children}</div>
    </div>
  );
};

export default Layout;
// career-up/apps/network/src/atoms.ts

import { atom } from "recoil";
import { type MyNetworkType } from "./types";

export const myNetworkAtom = atom<MyNetworkType | null>({
  key: "my-network",
  default: null,
});
// career-up/apps/network/src/containers/my-network-container.tsx

import React, { useCallback } from "react";
import { useRecoilState } from "recoil";
import { myNetworkAtom } from "../atoms";
import MyNetwork from "../components/my-network";
import useAuth0Client from "../hooks/use-auth0-client";
import { getMyNetwork } from "../apis";

const MyNetworkContainer: React.FC = () => {
  const auth0Client = useAuth0Client();
  const [myNetwork, setMyNetwork] = useRecoilState(myNetworkAtom);

  const fetchMyNetwork = useCallback(async () => {
    try {
      const token = await auth0Client.getTokenSilently();
      const myNetwork = await getMyNetwork(token);
      setMyNetwork(myNetwork);
    } catch (error) {
      alert(error);
    }
  }, [auth0Client, setMyNetwork]);

  return <MyNetwork myNetwork={myNetwork} fetchMyNetwork={fetchMyNetwork} />;
};

export default MyNetworkContainer;
// career-up/apps/network/src/components/my-network.css.ts

import { style } from "../styles/f.css";

export const topAreaTitle = style([
  "text-base",
  "font-bold",
  "h-4",
  "leading-4",
]);

export const topArea = style([
  "flex",
  "flex-col",
  "bg-white",
  "px-3",
  "py-4",
  "border-solid",
  "border-b",
  "border-slate-700",
  "rounded-tr-lg",
  "rounded-tl-lg",
  "gap-2",
]);

export const topAreaLinks = style([
  "flex",
  "flex-col",
  "bg-white",
  "py-2",
  "border-b",
]);

export const topAreaLink = [
  style([
    "flex",
    "flex-row",
    "text-base",
    "text-gray-600",
    "py-2",
    "px-3",
    "justify-between",
  ]),
  "hover:" + style(["bg-gray-200", "cursor-pointer"]),
].join(" ");

export const topAreaLinkCount = style(["text-base", "text-gray-600"]);

export const img = style(["w-12"]);

export const name = style(["text-base", "font-bold"]);

export const email = style(["text-xs", "text-gray-600"]);

export const bottomArea = style([
  "flex",
  "flex-col",
  "bg-white",
  "px-3",
  "py-4",
  "rounded-br-lg",
  "rounded-bl-lg",
  "gap-2",
  "justify-center",
  "items-center",
]);
// career-up/apps/network/src/components/my-network.tsx

import React, { useEffect } from "react";
import * as css from "./my-network.css";
import { type MyNetworkType } from "../types";

interface MyNetworkProps {
  myNetwork: MyNetworkType | null;
  fetchMyNetwork: () => Promise<void>;
}

const MyNetwork: React.FC<MyNetworkProps> = ({ myNetwork, fetchMyNetwork }) => {
  useEffect(() => {
    fetchMyNetwork();
  }, [fetchMyNetwork]);

  if (myNetwork === null) {
    return null;
  }

  const {
    connectionCount,
    contactCount,
    eventCount,
    pageCount,
    user: { picture, name, email },
  } = myNetwork;

  return (
    <div>
      <div className={css.topArea}>
        <span className={css.topAreaTitle}>인맥 관리</span>
      </div>
      <div className={css.topAreaLinks}>
        <div className={css.topAreaLink}>
          <div>1촌</div>
          <div className={css.topAreaLinkCount}>{connectionCount}</div>
        </div>
        <div className={css.topAreaLink}>
          <div>연락처</div>
          <div className={css.topAreaLinkCount}>{contactCount}</div>
        </div>
        <div className={css.topAreaLink}>
          <div>이벤트</div>
          <div className={css.topAreaLinkCount}>{eventCount}</div>
        </div>
        <div className={css.topAreaLink}>
          <div>페이지</div>
          <div className={css.topAreaLinkCount}>{pageCount}</div>
        </div>
      </div>
      <div className={css.bottomArea}>
        <img className={css.img} src={picture} />
        <div className={css.name}>{name}</div>
        <div className={css.email}>{email}</div>
      </div>
    </div>
  );
};

export default MyNetwork;

홈 페이지 작업

// career-up/apps/network/src/pages/page-home.tsx

import React from "react";
import ManageConnection from "../components/manage-connection";
import ConnectionsContainer from "../containers/connections-container";

const PageHome: React.FC = () => {
  return (
    <>
      <ManageConnection />
      <ConnectionsContainer />
    </>
  );
};

export default PageHome;
// career-up/apps/network/src/components/manage-connection.css.ts

import { style } from "../styles/f.css";

export const wrapper = style([
  "flex",
  "flex-row",
  "bg-white",
  "p-4",
  "rounded-lg",
  "justify-between",
  "items-center",
]);