├── .gitignore ├── README.md ├── assets └── photos │ ├── 00100.jpg │ ├── 00101.jpg │ ├── 00102.jpg │ ├── 00103.jpg │ ├── 00104.jpg │ ├── 00105.jpg │ ├── 00200.jpg │ ├── 00201.jpg │ ├── 00202.jpg │ ├── 00203.jpg │ ├── 00204.jpg │ ├── 00205.jpg │ ├── 00206.jpg │ ├── 00207.jpg │ ├── 00208.jpg │ ├── 00209.jpg │ ├── 00210.jpg │ ├── 00300.jpg │ ├── 00301.jpg │ ├── 00302.jpg │ ├── 00400.jpg │ ├── 00401.jpg │ ├── 00402.jpg │ ├── 00404.jpg │ ├── 00406.jpg │ ├── 00500.jpg │ ├── 00501.jpg │ ├── 00502.jpg │ ├── 00503.jpg │ ├── 00504.jpg │ ├── 00505.jpg │ ├── 00600.jpg │ ├── 00700.jpg │ ├── 00701.jpg │ ├── 00702.jpg │ ├── 00703.jpg │ ├── 00704.jpg │ ├── 00705.jpg │ ├── 00706.jpg │ ├── 00800.jpg │ ├── 00801.jpg │ ├── 00802.jpg │ ├── 00803.jpg │ ├── 00804.jpg │ ├── 00805.jpg │ ├── 00900.jpg │ ├── 00901.jpg │ ├── 00902.jpg │ ├── 01000.jpg │ ├── 01001.jpg │ ├── 01100.jpg │ ├── 01101.jpg │ ├── 01102.jpg │ ├── 01200.jpg │ ├── 01201.jpg │ ├── 01202.jpg │ ├── 01300.jpg │ ├── 01301.jpg │ ├── 01302.jpg │ ├── 01400.jpg │ ├── 01401.jpg │ ├── 01402.jpg │ ├── 01403.jpg │ ├── 01404.jpg │ ├── 01405.jpg │ ├── 01406.jpg │ ├── 01407.jpg │ ├── 01500.jpg │ ├── 01501.jpg │ ├── 01502.jpg │ ├── 01503.jpg │ ├── 01504.jpg │ ├── 01505.jpg │ ├── 01506.jpg │ ├── 01507.jpg │ ├── 01508.jpg │ ├── 01509.jpg │ ├── 01510.jpg │ ├── 01511.jpg │ ├── 01512.jpg │ ├── 01513.jpg │ ├── 01514.jpg │ ├── 01515.jpg │ ├── 01516.jpg │ ├── 01517.jpg │ ├── 01518.jpg │ ├── 01519.jpg │ └── 01520.jpg ├── docker-compose.yaml ├── package-lock.json ├── package.json └── packages ├── sns-api-1 ├── .env ├── .eslintrc.json ├── .gitignore ├── README.md ├── next.config.mjs ├── package.json ├── src │ ├── _mock │ │ ├── categories.ts │ │ ├── comments.ts │ │ ├── index.ts │ │ └── photos.ts │ ├── app │ │ └── api │ │ │ ├── categories │ │ │ ├── [categoryName] │ │ │ │ └── route.ts │ │ │ ├── id │ │ │ │ └── [categoryId] │ │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ │ └── photos │ │ │ ├── [photoId] │ │ │ ├── comments │ │ │ │ └── route.ts │ │ │ ├── edit │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ │ └── route.ts │ └── lib │ │ └── util │ │ └── index.ts └── tsconfig.json ├── sns-api-2 ├── .env ├── .eslintrc.json ├── .gitignore ├── README.md ├── next.config.mjs ├── package-lock.json ├── package.json ├── prisma │ ├── migrations │ │ ├── 20231111115350_init │ │ │ └── migration.sql │ │ └── migration_lock.toml │ ├── schema.prisma │ └── seed │ │ ├── category │ │ ├── fixture.json │ │ └── index.ts │ │ ├── comment │ │ ├── fixture.json │ │ └── index.ts │ │ ├── index.ts │ │ ├── like │ │ ├── fixture.json │ │ └── index.ts │ │ └── photo │ │ ├── fixture.json │ │ └── index.ts ├── src │ ├── app │ │ └── api │ │ │ ├── categories │ │ │ ├── [categoryName] │ │ │ │ └── route.ts │ │ │ ├── id │ │ │ │ └── [categoryId] │ │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ │ └── photos │ │ │ ├── [photoId] │ │ │ ├── comments │ │ │ │ └── route.ts │ │ │ ├── edit │ │ │ │ └── route.ts │ │ │ ├── like │ │ │ │ └── route.ts │ │ │ └── route.ts │ │ │ └── route.ts │ └── lib │ │ ├── prisma │ │ └── index.ts │ │ └── util │ │ ├── index.ts │ │ └── pagination.ts └── tsconfig.json ├── sns-shared-ui ├── .eslintrc.json ├── .gitignore ├── .storybook │ ├── main.ts │ └── preview.ts ├── env.d.ts ├── package.json ├── public │ └── images │ │ ├── account.svg │ │ └── photo-example.jpg ├── src │ ├── components │ │ ├── Account │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Accounts │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── AlertDialogModal │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── AlertLabel │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── AlertText │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Avatar │ │ │ ├── assets │ │ │ │ └── account.svg │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Button │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── ButtonCircle │ │ │ ├── assets │ │ │ │ └── plus.svg │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── CardContainer │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── CommentBox │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── HeadGroup │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Heading │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Icon │ │ │ ├── assets │ │ │ │ ├── account.svg │ │ │ │ ├── alert.svg │ │ │ │ ├── camera.svg │ │ │ │ ├── comments.svg │ │ │ │ ├── gear.svg │ │ │ │ ├── heart-border.svg │ │ │ │ ├── heart.svg │ │ │ │ ├── home.svg │ │ │ │ ├── paper-plane.svg │ │ │ │ ├── photos.svg │ │ │ │ ├── trash-box.svg │ │ │ │ ├── upload.svg │ │ │ │ ├── user.svg │ │ │ │ └── zoom.svg │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Label │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Layout │ │ │ ├── Container │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── Footer │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── Header │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── Main │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── Navigation │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── Root │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ └── index.tsx │ │ ├── LikeButton │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── LinkButton │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── LinkTag │ │ │ ├── index.stories.tsx │ │ │ └── index.tsx │ │ ├── LoadingModal │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── LoadingModule │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── LoadingSpinner │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── NotFound │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Pagination │ │ │ ├── assets │ │ │ │ ├── arrow-left.svg │ │ │ │ └── arrow-right.svg │ │ │ ├── index.tsx │ │ │ ├── pagination.ts │ │ │ └── style.module.css │ │ ├── PhotoCard │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── PhotoDndUploader │ │ │ ├── fns.ts │ │ │ └── index.tsx │ │ ├── ProfilePanel │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Section │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Select │ │ │ ├── assets │ │ │ │ └── arrow.svg │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── Tag │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── TextArea │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── TextField │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ └── Typography │ │ │ ├── index.stories.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ ├── globals.css │ └── index.tsx └── tsconfig.json ├── sns-web-1 ├── .env ├── .eslintrc.json ├── .gitignore ├── README.md ├── next-auth.d.ts ├── next.config.mjs ├── package.json ├── public │ ├── images │ │ ├── account.svg │ │ └── no-image.jpg │ ├── next.svg │ └── vercel.svg ├── src │ ├── _mock │ │ ├── README.md │ │ ├── index.ts │ │ ├── loginUser.ts │ │ ├── profiles.ts │ │ └── users.ts │ ├── app │ │ ├── (site) │ │ │ ├── _components │ │ │ │ ├── TopCategories │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── style.module.css │ │ │ │ ├── TopPhotos │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── style.module.css │ │ │ │ └── TopUsers │ │ │ │ │ └── index.tsx │ │ │ ├── categories │ │ │ │ ├── [categoryName] │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── style.module.css │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── page.tsx │ │ │ ├── photos │ │ │ │ └── [photoId] │ │ │ │ │ ├── _components │ │ │ │ │ ├── PhotoHero │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── PhotoMain │ │ │ │ │ │ ├── CommentForm │ │ │ │ │ │ ├── action.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── state.ts │ │ │ │ │ │ ├── CommentList │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── style.module.css │ │ │ │ │ │ ├── DeleteButtonContainer │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── style.module.css │ │ │ │ │ ├── edit │ │ │ │ │ ├── _components │ │ │ │ │ │ └── PhotoForm │ │ │ │ │ │ │ ├── action.ts │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── style.module.css │ │ │ │ │ └── page.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── profile │ │ │ │ ├── _components │ │ │ │ │ ├── MyProfilePanel │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── style.module.css │ │ │ │ │ └── MyProfilePhotos │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── style.module.css │ │ │ │ ├── edit │ │ │ │ │ ├── _components │ │ │ │ │ │ └── MyProfileEditPanel │ │ │ │ │ │ │ ├── action.ts │ │ │ │ │ │ │ ├── fns.ts │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── style.module.css │ │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ │ ├── style.module.css │ │ │ └── users │ │ │ │ └── [screenName] │ │ │ │ └── page.tsx │ │ ├── (static) │ │ │ ├── company-info │ │ │ │ ├── page.tsx │ │ │ │ └── style.module.css │ │ │ ├── layout.tsx │ │ │ └── privacy-policy │ │ │ │ ├── page.tsx │ │ │ │ └── style.module.css │ │ ├── _components │ │ │ ├── LayoutNavigation │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── ModalContainer │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── PhotoCreateForm │ │ │ │ ├── PhotoMeta │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── style.module.css │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── PhotoCreateModal │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── PhotoCreateModalContainer │ │ │ │ └── index.tsx │ │ │ ├── PhotoDeleteForm │ │ │ │ └── index.tsx │ │ │ ├── PhotoViewModal │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ └── PhotoViewModalContainer │ │ │ │ └── index.tsx │ │ ├── _hooks │ │ │ ├── useKey.ts │ │ │ ├── useModal.ts │ │ │ └── usePagination.ts │ │ ├── api │ │ │ ├── categories │ │ │ │ └── route.ts │ │ │ └── photos │ │ │ │ ├── [photoId] │ │ │ │ ├── like │ │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── layout.tsx │ │ └── not-found.tsx │ ├── constants.ts │ └── services │ │ ├── deletePhoto │ │ └── index.ts │ │ ├── getCategories │ │ └── index.ts │ │ ├── getCategory │ │ └── index.ts │ │ ├── getCategoryById │ │ └── index.ts │ │ ├── getPhoto │ │ └── index.ts │ │ ├── getPhotoComments │ │ └── index.ts │ │ ├── getPhotos │ │ └── index.ts │ │ ├── index.ts │ │ ├── postPhotoComment │ │ └── index.ts │ │ ├── postPhotoEdit │ │ └── index.ts │ │ ├── postPhotos │ │ └── index.ts │ │ └── type.ts └── tsconfig.json ├── sns-web-2 ├── .env ├── .eslintrc.json ├── .gitignore ├── README.md ├── next-auth.d.ts ├── next.config.mjs ├── package.json ├── prisma │ ├── migrations │ │ ├── 20230727150734_init │ │ │ └── migration.sql │ │ ├── 20230727151352_test │ │ │ └── migration.sql │ │ └── migration_lock.toml │ ├── schema.prisma │ └── seed │ │ ├── account │ │ ├── fixture.json │ │ └── index.ts │ │ ├── index.ts │ │ ├── profile │ │ ├── fixture.json │ │ └── index.ts │ │ └── user │ │ ├── fixture.json │ │ └── index.ts ├── public │ ├── images │ │ └── account.svg │ ├── next.svg │ └── vercel.svg ├── src │ ├── app │ │ ├── (site) │ │ │ ├── _components │ │ │ │ ├── TopCategories │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── style.module.css │ │ │ │ ├── TopPhotos │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── style.module.css │ │ │ │ └── TopUsers │ │ │ │ │ └── index.tsx │ │ │ ├── categories │ │ │ │ ├── [categoryName] │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── style.module.css │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── not-found.tsx │ │ │ ├── page.tsx │ │ │ ├── photos │ │ │ │ └── [photoId] │ │ │ │ │ ├── _components │ │ │ │ │ ├── PhotoComment │ │ │ │ │ │ ├── CommentForm │ │ │ │ │ │ │ ├── action.ts │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── state.ts │ │ │ │ │ │ ├── CommentList │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── style.module.css │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── PhotoHero │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── PhotoMeta │ │ │ │ │ │ ├── DeleteButtonContainer │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── style.module.css │ │ │ │ │ ├── edit │ │ │ │ │ ├── _components │ │ │ │ │ │ └── PhotoForm │ │ │ │ │ │ │ ├── action.ts │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── state.ts │ │ │ │ │ │ │ └── style.module.css │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── style.module.css │ │ │ ├── profile │ │ │ │ ├── _components │ │ │ │ │ ├── MyProfilePanel │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── style.module.css │ │ │ │ │ └── MyProfilePhotos │ │ │ │ │ │ ├── ClientMyProfilePhotos.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── style.module.css │ │ │ │ ├── edit │ │ │ │ │ ├── _components │ │ │ │ │ │ └── MyProfileEditPanel │ │ │ │ │ │ │ ├── MyProfileEditForm │ │ │ │ │ │ │ ├── EditAvatar │ │ │ │ │ │ │ │ ├── fns.ts │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── style.module.css │ │ │ │ │ │ │ ├── EditMeta │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── style.module.css │ │ │ │ │ │ │ ├── action.ts │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── state.ts │ │ │ │ │ │ │ └── style.module.css │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ │ ├── style.module.css │ │ │ └── users │ │ │ │ └── [screenName] │ │ │ │ ├── dataFetch.ts │ │ │ │ └── page.tsx │ │ ├── (static) │ │ │ ├── company-info │ │ │ │ ├── page.tsx │ │ │ │ └── style.module.css │ │ │ ├── layout.tsx │ │ │ ├── not-found.tsx │ │ │ └── privacy-policy │ │ │ │ ├── page.tsx │ │ │ │ └── style.module.css │ │ ├── _components │ │ │ ├── LayoutNavigation │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── LikeButtonContainer │ │ │ │ └── index.tsx │ │ │ ├── ModalContainer │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── PhotoCreateForm │ │ │ │ ├── PhotoMeta │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── style.module.css │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── PhotoCreateModal │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── PhotoCreateModalContainer │ │ │ │ └── index.tsx │ │ │ ├── PhotoDeleteForm │ │ │ │ └── index.tsx │ │ │ ├── PhotoViewModal │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ └── PhotoViewModalContainer │ │ │ │ └── index.tsx │ │ ├── _hooks │ │ │ ├── useKey.ts │ │ │ ├── useModal.ts │ │ │ └── usePagination.ts │ │ ├── api │ │ │ ├── auth │ │ │ │ └── [...nextauth] │ │ │ │ │ └── route.ts │ │ │ ├── categories │ │ │ │ └── route.ts │ │ │ └── photos │ │ │ │ ├── [photoId] │ │ │ │ ├── like │ │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ │ │ ├── route.ts │ │ │ │ └── upload │ │ │ │ └── route.ts │ │ ├── favicon.ico │ │ ├── global-error.tsx │ │ ├── layout.tsx │ │ └── not-found.tsx │ ├── constants.ts │ ├── lib │ │ ├── auth.ts │ │ ├── prisma.ts │ │ └── s3.ts │ ├── middleware.ts │ └── services │ │ ├── deletePhoto │ │ └── index.ts │ │ ├── getCategories │ │ └── index.ts │ │ ├── getCategory │ │ └── index.ts │ │ ├── getCategoryById │ │ └── index.ts │ │ ├── getPhoto │ │ └── index.ts │ │ ├── getPhotoComments │ │ └── index.ts │ │ ├── getPhotoLike │ │ └── index.ts │ │ ├── getPhotos │ │ └── index.ts │ │ ├── index.ts │ │ ├── postPhotoComment │ │ └── index.ts │ │ ├── postPhotoEdit │ │ └── index.ts │ │ ├── postPhotoLike │ │ └── index.ts │ │ ├── postPhotos │ │ └── index.ts │ │ └── type.ts └── tsconfig.json └── sns-web-3 ├── .env ├── .eslintrc.json ├── .gitignore ├── README.md ├── next-auth.d.ts ├── next.config.mjs ├── package.json ├── prisma ├── migrations │ ├── 20230727150734_init │ │ └── migration.sql │ ├── 20230727151352_test │ │ └── migration.sql │ └── migration_lock.toml ├── schema.prisma └── seed │ ├── account │ ├── fixture.json │ └── index.ts │ ├── index.ts │ ├── profile │ ├── fixture.json │ └── index.ts │ └── user │ ├── fixture.json │ └── index.ts ├── public ├── images │ └── account.svg ├── next.svg └── vercel.svg ├── src ├── app │ ├── (site) │ │ ├── @modal │ │ │ ├── (.)photos │ │ │ │ └── [photoId] │ │ │ │ │ └── view │ │ │ │ │ ├── LikeButtonForm │ │ │ │ │ ├── actions.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── state.ts │ │ │ │ │ └── style.module.css │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── style.module.css │ │ │ ├── _components │ │ │ │ └── ModalOverlay │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── style.module.css │ │ │ ├── default.tsx │ │ │ └── loading.tsx │ │ ├── _components │ │ │ ├── TopCategories │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── TopPhotos │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ └── TopUsers │ │ │ │ └── index.tsx │ │ ├── categories │ │ │ ├── [...segments] │ │ │ │ ├── page.tsx │ │ │ │ └── style.module.css │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── page.tsx │ │ ├── photos │ │ │ └── [photoId] │ │ │ │ ├── _components │ │ │ │ ├── PhotoComment │ │ │ │ │ ├── CommentForm │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── CommentList │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── style.module.css │ │ │ │ │ ├── action.ts │ │ │ │ │ ├── client.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── state.ts │ │ │ │ ├── PhotoHero │ │ │ │ │ ├── LikeButtonForm │ │ │ │ │ │ ├── actions.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── state.ts │ │ │ │ │ └── index.tsx │ │ │ │ └── PhotoMeta │ │ │ │ │ ├── DeleteButtonContainer │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── style.module.css │ │ │ │ ├── edit │ │ │ │ ├── _components │ │ │ │ │ └── PhotoForm │ │ │ │ │ │ ├── action.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ ├── state.ts │ │ │ │ │ │ ├── style.module.css │ │ │ │ │ │ └── validate.ts │ │ │ │ └── page.tsx │ │ │ │ ├── page.tsx │ │ │ │ ├── style.module.css │ │ │ │ └── view │ │ │ │ └── page.tsx │ │ ├── profile │ │ │ ├── _components │ │ │ │ ├── MyProfilePanel │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── style.module.css │ │ │ │ └── MyProfilePhotos │ │ │ │ │ ├── ClientMyProfilePhotos.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── style.module.css │ │ │ ├── edit │ │ │ │ ├── _components │ │ │ │ │ └── MyProfileEditPanel │ │ │ │ │ │ ├── MyProfileEditForm │ │ │ │ │ │ ├── EditAvatar │ │ │ │ │ │ │ ├── fns.ts │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── style.module.css │ │ │ │ │ │ ├── EditMeta │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── style.module.css │ │ │ │ │ │ ├── action.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── state.ts │ │ │ │ │ │ └── style.module.css │ │ │ │ │ │ └── index.tsx │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── style.module.css │ │ └── users │ │ │ └── [screenName] │ │ │ ├── _components │ │ │ ├── UserPanel │ │ │ │ └── index.tsx │ │ │ └── UserPhotos │ │ │ │ └── index.tsx │ │ │ ├── dataFetch.ts │ │ │ └── page.tsx │ ├── (static) │ │ ├── company-info │ │ │ ├── page.tsx │ │ │ └── style.module.css │ │ ├── layout.tsx │ │ └── privacy-policy │ │ │ ├── page.tsx │ │ │ └── style.module.css │ ├── _components │ │ ├── ClientRootLayout │ │ │ └── index.tsx │ │ ├── LayoutHeader │ │ │ └── index.tsx │ │ ├── LayoutNavigation │ │ │ ├── ClientLayoutNavigation.tsx │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── ModalContainer │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── PhotoCreateForm │ │ │ ├── PhotoMeta │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── actions.ts │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── PhotoCreateModal │ │ │ ├── index.tsx │ │ │ └── style.module.css │ │ ├── PhotoCreateModalContainer │ │ │ └── index.tsx │ │ ├── PhotoDeleteForm │ │ │ ├── actions.ts │ │ │ └── index.tsx │ │ └── PhotoViewNavigator │ │ │ ├── container.tsx │ │ │ ├── index.tsx │ │ │ └── provider.tsx │ ├── _hooks │ │ ├── useKey.ts │ │ ├── useModal.ts │ │ └── usePagination.ts │ ├── api │ │ ├── auth │ │ │ └── [...nextauth] │ │ │ │ └── route.ts │ │ └── photos │ │ │ └── upload │ │ │ └── route.ts │ ├── error.tsx │ ├── favicon.ico │ ├── fonts.ts │ ├── layout.tsx │ └── not-found.tsx ├── constants.ts ├── lib │ ├── auth.ts │ ├── prisma.ts │ └── s3.ts ├── middleware.ts └── services │ ├── deletePhoto │ └── index.ts │ ├── getCategories │ └── index.ts │ ├── getCategory │ └── index.ts │ ├── getCategoryById │ └── index.ts │ ├── getPhoto │ └── index.ts │ ├── getPhotoComments │ └── index.ts │ ├── getPhotoLike │ └── index.ts │ ├── getPhotos │ └── index.ts │ ├── index.ts │ ├── postPhotoComment │ └── index.ts │ ├── postPhotoEdit │ └── index.ts │ ├── postPhotoLike │ └── index.ts │ ├── postPhotos │ └── index.ts │ └── type.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .minio 3 | .pdf 4 | .vscode 5 | *.psd 6 | tsconfig.tsbuildinfo 7 | node_modules 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # practical-nextjs-book/applications 2 | 3 | 写真投稿SNSアプリのサンプルコードです。各 package は、書籍構成に応じて段階的に機能強化されています。 4 | 5 | ## SNS アプリサンプル 6 | 7 | - sns-web-1: スタブデータを表示する App(第6章) 8 | - sns-web-2: Prisma を導入したApp(6章〜10章) 9 | - sns-web-3: 改善された App(6章〜10章) 10 | 11 | ## SNS アプリサンプル用 APIサーバー 12 | 13 | - sns-api-1: DBなし(第6章) 14 | - sns-api-2: DBあり、Prisma を使用(6章〜10章) 15 | 16 | ## 共通UIライブラリ 17 | 18 | - sns-shared-ui: UI ライブラリ(6章〜10章) 19 | -------------------------------------------------------------------------------- /assets/photos/00100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00100.jpg -------------------------------------------------------------------------------- /assets/photos/00101.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00101.jpg -------------------------------------------------------------------------------- /assets/photos/00102.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00102.jpg -------------------------------------------------------------------------------- /assets/photos/00103.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00103.jpg -------------------------------------------------------------------------------- /assets/photos/00104.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00104.jpg -------------------------------------------------------------------------------- /assets/photos/00105.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00105.jpg -------------------------------------------------------------------------------- /assets/photos/00200.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00200.jpg -------------------------------------------------------------------------------- /assets/photos/00201.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00201.jpg -------------------------------------------------------------------------------- /assets/photos/00202.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00202.jpg -------------------------------------------------------------------------------- /assets/photos/00203.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00203.jpg -------------------------------------------------------------------------------- /assets/photos/00204.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00204.jpg -------------------------------------------------------------------------------- /assets/photos/00205.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00205.jpg -------------------------------------------------------------------------------- /assets/photos/00206.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00206.jpg -------------------------------------------------------------------------------- /assets/photos/00207.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00207.jpg -------------------------------------------------------------------------------- /assets/photos/00208.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00208.jpg -------------------------------------------------------------------------------- /assets/photos/00209.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00209.jpg -------------------------------------------------------------------------------- /assets/photos/00210.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00210.jpg -------------------------------------------------------------------------------- /assets/photos/00300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00300.jpg -------------------------------------------------------------------------------- /assets/photos/00301.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00301.jpg -------------------------------------------------------------------------------- /assets/photos/00302.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00302.jpg -------------------------------------------------------------------------------- /assets/photos/00400.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00400.jpg -------------------------------------------------------------------------------- /assets/photos/00401.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00401.jpg -------------------------------------------------------------------------------- /assets/photos/00402.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00402.jpg -------------------------------------------------------------------------------- /assets/photos/00404.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00404.jpg -------------------------------------------------------------------------------- /assets/photos/00406.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00406.jpg -------------------------------------------------------------------------------- /assets/photos/00500.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00500.jpg -------------------------------------------------------------------------------- /assets/photos/00501.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00501.jpg -------------------------------------------------------------------------------- /assets/photos/00502.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00502.jpg -------------------------------------------------------------------------------- /assets/photos/00503.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00503.jpg -------------------------------------------------------------------------------- /assets/photos/00504.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00504.jpg -------------------------------------------------------------------------------- /assets/photos/00505.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00505.jpg -------------------------------------------------------------------------------- /assets/photos/00600.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00600.jpg -------------------------------------------------------------------------------- /assets/photos/00700.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00700.jpg -------------------------------------------------------------------------------- /assets/photos/00701.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00701.jpg -------------------------------------------------------------------------------- /assets/photos/00702.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00702.jpg -------------------------------------------------------------------------------- /assets/photos/00703.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00703.jpg -------------------------------------------------------------------------------- /assets/photos/00704.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00704.jpg -------------------------------------------------------------------------------- /assets/photos/00705.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00705.jpg -------------------------------------------------------------------------------- /assets/photos/00706.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00706.jpg -------------------------------------------------------------------------------- /assets/photos/00800.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00800.jpg -------------------------------------------------------------------------------- /assets/photos/00801.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00801.jpg -------------------------------------------------------------------------------- /assets/photos/00802.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00802.jpg -------------------------------------------------------------------------------- /assets/photos/00803.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00803.jpg -------------------------------------------------------------------------------- /assets/photos/00804.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00804.jpg -------------------------------------------------------------------------------- /assets/photos/00805.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00805.jpg -------------------------------------------------------------------------------- /assets/photos/00900.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00900.jpg -------------------------------------------------------------------------------- /assets/photos/00901.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00901.jpg -------------------------------------------------------------------------------- /assets/photos/00902.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/00902.jpg -------------------------------------------------------------------------------- /assets/photos/01000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01000.jpg -------------------------------------------------------------------------------- /assets/photos/01001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01001.jpg -------------------------------------------------------------------------------- /assets/photos/01100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01100.jpg -------------------------------------------------------------------------------- /assets/photos/01101.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01101.jpg -------------------------------------------------------------------------------- /assets/photos/01102.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01102.jpg -------------------------------------------------------------------------------- /assets/photos/01200.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01200.jpg -------------------------------------------------------------------------------- /assets/photos/01201.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01201.jpg -------------------------------------------------------------------------------- /assets/photos/01202.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01202.jpg -------------------------------------------------------------------------------- /assets/photos/01300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01300.jpg -------------------------------------------------------------------------------- /assets/photos/01301.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01301.jpg -------------------------------------------------------------------------------- /assets/photos/01302.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01302.jpg -------------------------------------------------------------------------------- /assets/photos/01400.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01400.jpg -------------------------------------------------------------------------------- /assets/photos/01401.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01401.jpg -------------------------------------------------------------------------------- /assets/photos/01402.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01402.jpg -------------------------------------------------------------------------------- /assets/photos/01403.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01403.jpg -------------------------------------------------------------------------------- /assets/photos/01404.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01404.jpg -------------------------------------------------------------------------------- /assets/photos/01405.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01405.jpg -------------------------------------------------------------------------------- /assets/photos/01406.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01406.jpg -------------------------------------------------------------------------------- /assets/photos/01407.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01407.jpg -------------------------------------------------------------------------------- /assets/photos/01500.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01500.jpg -------------------------------------------------------------------------------- /assets/photos/01501.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01501.jpg -------------------------------------------------------------------------------- /assets/photos/01502.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01502.jpg -------------------------------------------------------------------------------- /assets/photos/01503.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01503.jpg -------------------------------------------------------------------------------- /assets/photos/01504.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01504.jpg -------------------------------------------------------------------------------- /assets/photos/01505.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01505.jpg -------------------------------------------------------------------------------- /assets/photos/01506.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01506.jpg -------------------------------------------------------------------------------- /assets/photos/01507.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01507.jpg -------------------------------------------------------------------------------- /assets/photos/01508.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01508.jpg -------------------------------------------------------------------------------- /assets/photos/01509.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01509.jpg -------------------------------------------------------------------------------- /assets/photos/01510.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01510.jpg -------------------------------------------------------------------------------- /assets/photos/01511.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01511.jpg -------------------------------------------------------------------------------- /assets/photos/01512.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01512.jpg -------------------------------------------------------------------------------- /assets/photos/01513.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01513.jpg -------------------------------------------------------------------------------- /assets/photos/01514.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01514.jpg -------------------------------------------------------------------------------- /assets/photos/01515.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01515.jpg -------------------------------------------------------------------------------- /assets/photos/01516.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01516.jpg -------------------------------------------------------------------------------- /assets/photos/01517.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01517.jpg -------------------------------------------------------------------------------- /assets/photos/01518.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01518.jpg -------------------------------------------------------------------------------- /assets/photos/01519.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01519.jpg -------------------------------------------------------------------------------- /assets/photos/01520.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/assets/photos/01520.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ps-web", 3 | "private": true, 4 | "engines": { 5 | "node": ">=18.17.0" 6 | }, 7 | "workspaces": [ 8 | "packages/sns-api-1", 9 | "packages/sns-shared-ui", 10 | "packages/sns-web-1", 11 | "packages/sns-web-2", 12 | "packages/sns-web-3" 13 | ], 14 | "devDependencies": { 15 | "npm-run-all": "^4.1.5", 16 | "rimraf": "^5.0.5" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/sns-api-1/.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL="postgresql://root:password@127.0.0.1:5433/app-db?schema=public" 2 | -------------------------------------------------------------------------------- /packages/sns-api-1/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /packages/sns-api-1/README.md: -------------------------------------------------------------------------------- 1 | # PS-API/api-1 2 | 3 | スタブデータを返却する API サーバー 4 | -------------------------------------------------------------------------------- /packages/sns-api-1/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | } 4 | 5 | export default nextConfig; 6 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/_mock/categories.ts: -------------------------------------------------------------------------------- 1 | import type { Category } from "."; 2 | 3 | export const categories: Category[] = [ 4 | { 5 | id: "1", 6 | name: "flower", 7 | label: "花", 8 | description: "description", 9 | imageUrl: "/images/no-image.jpg", 10 | }, 11 | { 12 | id: "2", 13 | name: "animal", 14 | label: "動物", 15 | description: "description", 16 | imageUrl: "/images/no-image.jpg", 17 | }, 18 | { 19 | id: "3", 20 | name: "landscape", 21 | label: "風景", 22 | description: "description", 23 | imageUrl: "/images/no-image.jpg", 24 | }, 25 | ]; 26 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/_mock/comments.ts: -------------------------------------------------------------------------------- 1 | import type { Comment } from "."; 2 | 3 | export const comments: Comment[] = [ 4 | // { id: "1", authorId: "authorId", photoId: "photoId", comment: "comment" }, 5 | ]; 6 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/_mock/index.ts: -------------------------------------------------------------------------------- 1 | export type Photo = { 2 | id: string; 3 | title: string; 4 | description: string; 5 | imageUrl: string; 6 | authorId: string; 7 | categoryId: string; 8 | }; 9 | 10 | export type Category = { 11 | id: string; 12 | name: string; 13 | label: string; 14 | description: string; 15 | imageUrl: string; 16 | }; 17 | 18 | export type Comment = { 19 | id: string; 20 | authorId: string; 21 | photoId: string; 22 | comment: string; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/_mock/photos.ts: -------------------------------------------------------------------------------- 1 | import type { Photo } from "."; 2 | 3 | export const photos: Photo[] = [...new Array(10)].map((_, i) => { 4 | const id = `00${i + 1}`.slice(-3); 5 | const authorId = `${(i % 3) + 1}`; 6 | return { 7 | id, 8 | title: `Test-${id}`, 9 | description: `Test-${id} description...`.repeat(10), 10 | imageUrl: "/images/no-image.jpg", 11 | authorId, 12 | categoryId: `${(i % 3) + 1}`, 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/app/api/categories/[categoryName]/route.ts: -------------------------------------------------------------------------------- 1 | import { categories } from "@/_mock/categories"; 2 | import type { NextRequest } from "next/server"; 3 | 4 | export async function GET( 5 | _: NextRequest, 6 | { params }: { params: { categoryName: string } }, 7 | ) { 8 | // 🚧: DBに接続しレコードを取得する 9 | const category = categories.find( 10 | (category) => category.name === params.categoryName, 11 | ); 12 | if (!category) { 13 | return Response.json({ message: "Not Found" }, { status: 404 }); 14 | } 15 | return Response.json({ category }); 16 | } 17 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/app/api/categories/id/[categoryId]/route.ts: -------------------------------------------------------------------------------- 1 | import { categories } from "@/_mock/categories"; 2 | import type { NextRequest } from "next/server"; 3 | 4 | export async function GET( 5 | _: NextRequest, 6 | { params }: { params: { categoryId: string } }, 7 | ) { 8 | // 🚧: DBに接続しレコードを取得する 9 | const category = categories.find( 10 | (category) => category.id === params.categoryId, 11 | ); 12 | if (!category) { 13 | return Response.json({ message: "Not Found" }, { status: 404 }); 14 | } 15 | return Response.json({ category }); 16 | } 17 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/app/api/categories/route.ts: -------------------------------------------------------------------------------- 1 | import { categories } from "@/_mock/categories"; 2 | 3 | export async function GET() { 4 | // 🚧: DBに接続しレコードを取得する 5 | return Response.json({ categories }); 6 | } 7 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/app/api/photos/[photoId]/comments/route.ts: -------------------------------------------------------------------------------- 1 | import type { Comment } from "@/_mock"; 2 | 3 | export async function POST() { 4 | // 🚧: DBに接続しレコードを作成する 5 | const comment: Comment = { 6 | id: "1", 7 | authorId: "authorId", 8 | photoId: "photoId", 9 | comment: "comment", 10 | }; 11 | return Response.json({ comment }); 12 | } 13 | 14 | export async function GET() { 15 | // 🚧: DBに接続しレコードを取得する 16 | return Response.json({ comments: [] }); 17 | } 18 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/app/api/photos/[photoId]/edit/route.ts: -------------------------------------------------------------------------------- 1 | import { photos } from "@/_mock/photos"; 2 | import type { NextRequest } from "next/server"; 3 | 4 | export async function POST( 5 | _: NextRequest, 6 | { params }: { params: { photoId: string } }, 7 | ) { 8 | // 🚧: DBに接続しレコードを更新する 9 | const photo = photos.find((photo) => photo.id === params.photoId); 10 | if (!photo) { 11 | return Response.json({ message: "Not Found" }, { status: 404 }); 12 | } 13 | return Response.json({ photo }); 14 | } 15 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/app/api/photos/route.ts: -------------------------------------------------------------------------------- 1 | import { photos } from "@/_mock/photos"; 2 | 3 | export async function GET(request: Request) { 4 | // 🚧: DBに接続しレコードを取得する 5 | const { searchParams } = new URL(request.url); 6 | const authorId = searchParams.get("authorId"); 7 | if (authorId) { 8 | return Response.json({ 9 | photos: photos.filter((photo) => photo.authorId === authorId), 10 | }); 11 | } 12 | return Response.json({ photos }); 13 | } 14 | 15 | export async function POST(request: Request) { 16 | // 🚧: DBに接続しレコードを更新する 17 | const { searchParams } = new URL(request.url); 18 | const authorId = searchParams.get("authorId"); 19 | if (authorId) { 20 | return Response.json({ 21 | photos: photos.filter((photo) => photo.authorId === authorId), 22 | }); 23 | } 24 | return Response.json({ photos }); 25 | } 26 | -------------------------------------------------------------------------------- /packages/sns-api-1/src/lib/util/index.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod"; 2 | import type { ZodSchema } from "zod"; 3 | 4 | export function parseAsPositiveInt(q: string | string[] | undefined) { 5 | const effect = z.number().positive().int(); 6 | const val = Number(q); 7 | try { 8 | effect.parse(val); 9 | return val; 10 | } catch { 11 | return undefined; 12 | } 13 | } 14 | 15 | export function parseAsNonEmptyString(q: string | string[] | undefined) { 16 | if (typeof q === "string" && q.length > 0) return q; 17 | } 18 | 19 | export function validate( 20 | target: unknown, 21 | schema: T, 22 | ): asserts target is z.infer { 23 | schema.parse(target); 24 | } 25 | -------------------------------------------------------------------------------- /packages/sns-api-1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./src/*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /packages/sns-api-2/.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL="postgresql://root:password@127.0.0.1:5433/app-db?schema=public" 2 | -------------------------------------------------------------------------------- /packages/sns-api-2/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /packages/sns-api-2/README.md: -------------------------------------------------------------------------------- 1 | # PS-API/api-2 2 | 3 | Prisma を使用し、機能を作り込んだ API サーバー 4 | -------------------------------------------------------------------------------- /packages/sns-api-2/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | } 4 | 5 | export default nextConfig; 6 | -------------------------------------------------------------------------------- /packages/sns-api-2/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /packages/sns-api-2/prisma/seed/category/index.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from ".."; 2 | import fixture from "./fixture.json"; 3 | import type { PrismaPromise, Category } from "@prisma/client"; 4 | 5 | export const category = () => { 6 | const res: PrismaPromise[] = []; 7 | fixture.forEach((data) => { 8 | const row = prisma.category.create({ data }); 9 | res.push(row); 10 | }); 11 | return res; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/sns-api-2/prisma/seed/comment/fixture.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "0", 4 | "authorId": "0", 5 | "photoId": "0", 6 | "comment": "This is a comment" 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /packages/sns-api-2/prisma/seed/comment/index.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from ".."; 2 | import fixture from "./fixture.json"; 3 | import type { PrismaPromise, Comment } from "@prisma/client"; 4 | 5 | export const comment = () => { 6 | const res: PrismaPromise[] = []; 7 | fixture.forEach((data) => { 8 | const row = prisma.comment.create({ data }); 9 | res.push(row); 10 | }); 11 | return res; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/sns-api-2/prisma/seed/index.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | import { category } from "./category"; 3 | import { photo } from "./photo"; 4 | 5 | export const prisma = new PrismaClient(); 6 | 7 | const main = async () => { 8 | console.log(`Start seeding ...`); 9 | await prisma.$transaction([ 10 | ...category(), 11 | ...photo(), 12 | ]); 13 | console.log(`Seeding finished.`); 14 | }; 15 | 16 | main() 17 | .catch((e) => { 18 | console.error(e); 19 | process.exit(1); 20 | }) 21 | .finally(async () => { 22 | await prisma.$disconnect(); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/sns-api-2/prisma/seed/like/fixture.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "0", 4 | "userId": "001", 5 | "photoId": "001" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /packages/sns-api-2/prisma/seed/like/index.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from ".."; 2 | import fixture from "./fixture.json"; 3 | import type { PrismaPromise, Like } from "@prisma/client"; 4 | 5 | export const like = () => { 6 | const res: PrismaPromise[] = []; 7 | fixture.forEach((data) => { 8 | const row = prisma.like.create({ data }); 9 | res.push(row); 10 | }); 11 | return res; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/sns-api-2/prisma/seed/photo/index.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from ".."; 2 | import fixture from "./fixture.json"; 3 | import type { PrismaPromise, Photo } from "@prisma/client"; 4 | 5 | export const photo = () => { 6 | const res: PrismaPromise[] = []; 7 | fixture.forEach((data) => { 8 | Array.from({ length: 1 }, (_, i) => { 9 | const row = prisma.photo.create({ 10 | data: { ...data, id: `${data.id}${i}` }, 11 | }); 12 | res.push(row); 13 | }); 14 | }); 15 | return res; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/sns-api-2/src/app/api/categories/route.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "@/lib/prisma"; 2 | 3 | export async function GET() { 4 | // ★: Category テーブルのレコードを全て取得する 5 | const categories = await prisma.category.findMany({ 6 | include: { _count: { select: { photos: true } } }, 7 | }); 8 | console.log(`GET: /api/categories ${new Date().toISOString()}`); 9 | return Response.json({ 10 | categories: categories.map(({ _count, ...category }) => ({ 11 | ...category, 12 | totalPhotoCount: _count.photos, 13 | })), 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /packages/sns-api-2/src/app/api/photos/[photoId]/edit/route.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "@/lib/prisma"; 2 | import type { NextRequest } from "next/server"; 3 | 4 | export async function POST( 5 | req: NextRequest, 6 | { params }: { params: { photoId: string } }, 7 | ) { 8 | const body = await req.json(); 9 | const { categoryId, userId, ...data } = body; 10 | const photo = await prisma.photo.update({ 11 | where: { id: params.photoId }, 12 | include: { category: true }, 13 | data: { 14 | ...data, 15 | authorId: userId, 16 | category: { 17 | connect: { id: categoryId }, 18 | }, 19 | }, 20 | }); 21 | return Response.json({ photo }); 22 | } 23 | -------------------------------------------------------------------------------- /packages/sns-api-2/src/lib/prisma/index.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient, Prisma } from "@prisma/client"; 2 | 3 | const prismaClientSingleton = () => { 4 | return new PrismaClient(); 5 | }; 6 | 7 | type PrismaClientSingleton = ReturnType; 8 | 9 | const globalForPrisma = globalThis as unknown as { 10 | prisma: PrismaClientSingleton | undefined; 11 | }; 12 | 13 | export const prisma = globalForPrisma.prisma ?? prismaClientSingleton(); 14 | 15 | if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma; 16 | 17 | export { Prisma }; 18 | -------------------------------------------------------------------------------- /packages/sns-api-2/src/lib/util/index.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod"; 2 | import type { ZodSchema } from "zod"; 3 | 4 | export function parseAsPositiveInt(q: string | string[] | undefined) { 5 | const effect = z.number().positive().int(); 6 | const val = Number(q); 7 | try { 8 | effect.parse(val); 9 | return val; 10 | } catch { 11 | return undefined; 12 | } 13 | } 14 | 15 | export function parseAsNonEmptyString(q: string | string[] | undefined) { 16 | if (typeof q === "string" && q.length > 0) return q; 17 | } 18 | 19 | export function validate( 20 | target: unknown, 21 | schema: T, 22 | ): asserts target is z.infer { 23 | schema.parse(target); 24 | } 25 | -------------------------------------------------------------------------------- /packages/sns-api-2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./src/*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/.gitignore: -------------------------------------------------------------------------------- 1 | .rollup.cache 2 | dist -------------------------------------------------------------------------------- /packages/sns-shared-ui/.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import type { Preview } from "@storybook/react"; 2 | import '../src/globals.css'; 3 | 4 | const preview: Preview = { 5 | parameters: { 6 | actions: { argTypesRegex: "^on[A-Z].*" }, 7 | controls: { 8 | matchers: { 9 | color: /(background|color)$/i, 10 | date: /Date$/, 11 | }, 12 | }, 13 | backgrounds: { 14 | default: 'light', 15 | values: [ 16 | { 17 | name: 'light', 18 | value: '#fff', 19 | }, 20 | { 21 | name: 'dark', 22 | value: '#333', 23 | }, 24 | ], 25 | }, 26 | }, 27 | 28 | }; 29 | 30 | export default preview; 31 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/public/images/account.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/public/images/photo-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practical-nextjs-book/applications/5977380114058b2dee591abecc567999bfd01f49/packages/sns-shared-ui/public/images/photo-example.jpg -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Account/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Account } from "./"; 2 | import type { Meta, StoryObj } from "@storybook/react"; 3 | 4 | const meta = { 5 | component: Account, 6 | } satisfies Meta; 7 | 8 | export default meta; 9 | type Story = StoryObj; 10 | 11 | export const NoName: Story = {}; 12 | 13 | export const OnlyName: Story = { 14 | args: { 15 | name: "John Doe", 16 | }, 17 | }; 18 | 19 | export const OnlyScreenName: Story = { 20 | args: { 21 | screenName: "johndoe", 22 | }, 23 | }; 24 | 25 | export const FullName: Story = { 26 | args: { 27 | name: "John Doe", 28 | screenName: "johndoe", 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Account/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Avatar } from "../Avatar"; 3 | import styles from "./style.module.css"; 4 | 5 | type Props = { 6 | name?: string | null; 7 | imageUrl?: string | null; 8 | screenName?: string | null; 9 | avatarSize?: "small" | "medium" | "large"; 10 | }; 11 | 12 | export function Account({ 13 | name, 14 | imageUrl, 15 | screenName, 16 | avatarSize = "medium", 17 | }: Props) { 18 | return ( 19 |
20 | 21 |
22 | {name &&

{name}

} 23 | {screenName &&

@{screenName}

} 24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Account/style.module.css: -------------------------------------------------------------------------------- 1 | .account { 2 | display: flex; 3 | align-items: center; 4 | } 5 | 6 | .info { 7 | flex-grow: 1; 8 | display: flex; 9 | flex-direction: column; 10 | padding: 0 8px; 11 | } 12 | 13 | .name { 14 | font-size: 16px; 15 | } 16 | 17 | .screenName { 18 | font-size: 14px; 19 | color: var(--gray-600); 20 | } 21 | 22 | @media screen and (max-width: 768px) { 23 | .name { 24 | font-size: 12px; 25 | } 26 | 27 | .screenName { 28 | font-size: 10px; 29 | } 30 | } -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Accounts/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "next/link"; 3 | import { Account } from "../Account"; 4 | import styles from "./style.module.css"; 5 | 6 | type Props = { 7 | users: { 8 | id: string; 9 | name?: string | null; 10 | imageUrl?: string | null; 11 | screenName?: string | null; 12 | }[]; 13 | }; 14 | 15 | export function Accounts({ users }: Props) { 16 | return ( 17 |
18 | {users.map(({ id, ...user }) => ( 19 | 24 | 25 | 26 | ))} 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Accounts/style.module.css: -------------------------------------------------------------------------------- 1 | .accounts { 2 | padding: 16px; 3 | border: 1px solid var(--gray-200); 4 | border-radius: var(--border-radius); 5 | } 6 | 7 | .user { 8 | display: flex; 9 | align-items: center; 10 | padding: 16px 0; 11 | border-bottom: 1px solid var(--gray-200); 12 | } 13 | 14 | .user:first-child { 15 | padding-top: 0; 16 | } 17 | 18 | .user:last-child { 19 | padding-bottom: 0; 20 | border-bottom: none; 21 | } -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/AlertDialogModal/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "../Button"; 2 | import { AlertDialogModal } from "./"; 3 | import type { Meta, StoryObj } from "@storybook/react"; 4 | 5 | const meta = { 6 | component: AlertDialogModal, 7 | args: { 8 | title: "削除します", 9 | }, 10 | } satisfies Meta; 11 | 12 | export default meta; 13 | type Story = StoryObj; 14 | 15 | export const Primary: Story = { 16 | args: { 17 | messageNode: "本当に削除しますか?", 18 | actionsNode: ( 19 | <> 20 | 21 | 22 | 23 | ), 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/AlertLabel/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { AlertLabel } from "./"; 2 | import type { Meta, StoryObj } from "@storybook/react"; 3 | 4 | const meta = { 5 | component: AlertLabel, 6 | args: { 7 | children: "Error!", 8 | }, 9 | } satisfies Meta; 10 | 11 | export default meta; 12 | type Story = StoryObj; 13 | 14 | export const Primary: Story = {}; 15 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/AlertLabel/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Icon } from "../Icon"; 3 | import styles from "./style.module.css"; 4 | import { AlertText } from "../AlertText"; 5 | 6 | type Props = { 7 | children: React.ReactNode; 8 | }; 9 | 10 | export function AlertLabel({ children }: Props) { 11 | return ( 12 | 13 | 14 | {children} 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/AlertLabel/style.module.css: -------------------------------------------------------------------------------- 1 | .label { 2 | display: flex; 3 | align-items: center; 4 | gap: 8px; 5 | padding: 16px; 6 | margin: 16px; 7 | background-color: var(--orange-50); 8 | border: 1px solid var(--orange-300); 9 | border-radius: var(--border-radius); 10 | } -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/AlertText/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { AlertText } from "./"; 2 | import type { Meta, StoryObj } from "@storybook/react"; 3 | 4 | const meta = { 5 | component: AlertText, 6 | args: { 7 | children: "Error!", 8 | }, 9 | } satisfies Meta; 10 | 11 | export default meta; 12 | type Story = StoryObj; 13 | 14 | export const Primary: Story = {}; 15 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/AlertText/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentProps } from "react"; 2 | import styles from "./style.module.css"; 3 | import { Typography } from "../Typography"; 4 | import clsx from "clsx"; 5 | 6 | type Props = ComponentProps; 7 | 8 | export function AlertText({ children, className }: Props) { 9 | return ( 10 | {children} 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/AlertText/style.module.css: -------------------------------------------------------------------------------- 1 | .text { 2 | color: var(--orange-800); 3 | } -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Avatar/assets/account.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Avatar/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Avatar } from "./"; 2 | import type { Meta, StoryObj } from "@storybook/react"; 3 | 4 | const meta = { 5 | component: Avatar, 6 | } satisfies Meta; 7 | 8 | export default meta; 9 | type Story = StoryObj; 10 | 11 | export const Primary: Story = {}; 12 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Avatar/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import clsx from "clsx"; 3 | import styles from "./style.module.css"; 4 | 5 | type Props = { 6 | size?: "small" | "medium" | "large"; 7 | avatarImageUrl?: string | null; 8 | }; 9 | 10 | export function Avatar({ size = "medium", avatarImageUrl }: Props) { 11 | return ( 12 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Button/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from "react"; 2 | import type { ComponentPropsWithoutRef } from "react"; 3 | import clsx from "clsx"; 4 | import styles from "./style.module.css"; 5 | 6 | type Props = ComponentPropsWithoutRef<"button"> & { 7 | size?: "xsmall" | "small" | "medium" | "large"; 8 | color?: "black" | "orange" | "gray" | "white"; 9 | }; 10 | 11 | export const Button = forwardRef(function ButtonBase( 12 | { className, size = "medium", color = "black", ...props }, 13 | ref, 14 | ) { 15 | return ( 16 | 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/CommentBox/style.module.css: -------------------------------------------------------------------------------- 1 | .commentBox { 2 | display: flex; 3 | padding: 8px; 4 | border: 1px solid var(--gray-200); 5 | border-radius: var(--border-radius); 6 | } 7 | 8 | .commentBox input { 9 | flex-grow: 1; 10 | border-right: 0; 11 | border-top-right-radius: 0; 12 | border-bottom-right-radius: 0; 13 | } 14 | 15 | .commentBox button { 16 | padding: 1em; 17 | border: 1px solid var(--gray-300); 18 | border-radius: var(--border-radius); 19 | border-top-left-radius: 0; 20 | border-bottom-left-radius: 0; 21 | background-color: transparent; 22 | cursor: pointer; 23 | } 24 | 25 | .commentBox button:hover { 26 | opacity: 0.5; 27 | } -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/HeadGroup/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "../Button"; 2 | import { Heading } from "../Heading"; 3 | import { HeadGroup } from "./"; 4 | import type { Meta, StoryObj } from "@storybook/react"; 5 | 6 | const meta = { 7 | component: HeadGroup, 8 | args: { 9 | children: ( 10 | <> 11 | 見出し 12 | 15 | 16 | ), 17 | }, 18 | } satisfies Meta; 19 | 20 | export default meta; 21 | type Story = StoryObj; 22 | 23 | export const Primary: Story = {}; 24 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/HeadGroup/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./style.module.css"; 3 | 4 | type Props = { 5 | children: React.ReactNode; 6 | }; 7 | 8 | export function HeadGroup({ children }: Props) { 9 | return
{children}
; 10 | } 11 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/HeadGroup/style.module.css: -------------------------------------------------------------------------------- 1 | .headGroup { 2 | display: flex; 3 | padding-bottom: 16px; 4 | align-items: center; 5 | } 6 | 7 | .headGroup>*:first-child { 8 | flex-grow: 1; 9 | } -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Heading/index.tsx: -------------------------------------------------------------------------------- 1 | import type React from "react"; 2 | import { type ComponentPropsWithRef, createElement } from "react"; 3 | import clsx from "clsx"; 4 | import styles from "./style.module.css"; 5 | 6 | type Size = "xsmall" | "small" | "medium" | "large"; 7 | type Props = { 8 | level: 1 | 2 | 3 | 4 | 5 | 6; 9 | size?: Size; 10 | children: React.ReactNode; 11 | } & ComponentPropsWithRef<"h1" | "h2" | "h3" | "h4" | "h5" | "h6">; 12 | 13 | export function Heading({ 14 | level, 15 | className, 16 | children, 17 | size = "medium", 18 | ...props 19 | }: Props) { 20 | return createElement( 21 | `h${level}`, 22 | { className: clsx(styles.title, styles[size], className), ...props }, 23 | children, 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Heading/style.module.css: -------------------------------------------------------------------------------- 1 | .title { 2 | display: block; 3 | font-size: 24px; 4 | font-weight: bolder; 5 | color: var(--gray-800); 6 | } 7 | 8 | .xsmall { 9 | font-size: 14px; 10 | } 11 | 12 | .small { 13 | font-size: 18px; 14 | } 15 | 16 | .medium { 17 | font-size: 24px; 18 | } 19 | 20 | .large { 21 | font-size: 32px; 22 | } 23 | 24 | @media screen and (max-width: 768px) { 25 | .xsmall { 26 | font-size: 12px; 27 | } 28 | 29 | .small { 30 | font-size: 14px; 31 | } 32 | 33 | .medium { 34 | font-size: 16px; 35 | } 36 | 37 | .large { 38 | font-size: 18px; 39 | } 40 | } -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/account.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/alert.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/camera.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/heart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/home.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/paper-plane.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/photos.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/trash-box.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/upload.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Icon/assets/zoom.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Label/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Label } from "./"; 2 | import type { Meta, StoryObj } from "@storybook/react"; 3 | 4 | const meta = { 5 | component: Label, 6 | args: { children: "見出し" }, 7 | } satisfies Meta; 8 | 9 | export default meta; 10 | type Story = StoryObj; 11 | 12 | export const Xsmall: Story = { 13 | args: { size: "xsmall" }, 14 | }; 15 | 16 | export const Small: Story = { 17 | args: { size: "small" }, 18 | }; 19 | 20 | export const Medium: Story = { 21 | args: { size: "medium" }, 22 | }; 23 | 24 | export const Large: Story = { 25 | args: { size: "large" }, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/sns-shared-ui/src/components/Label/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { forwardRef, type ComponentPropsWithoutRef } from "react"; 3 | import clsx from "clsx"; 4 | import styles from "./style.module.css"; 5 | 6 | type Props = ComponentPropsWithoutRef<"label"> & { 7 | size?: "xsmall" | "small" | "medium" | "large"; 8 | }; 9 | 10 | export const Label = forwardRef(function LabelBase( 11 | { size = "medium", className, ...props }, 12 | ref, 13 | ) { 14 | return ( 15 |