Compare commits
	
		
			No commits in common. "b16a677e653b73b0ff5b4c068dae728815262681" and "461df9d7bab8d3c25c98e4d1c40195ebc1ac6361" have entirely different histories.
		
	
	
		
			b16a677e65
			...
			461df9d7ba
		
	
		
@ -1,15 +1,10 @@
 | 
				
			|||||||
DATABASE_URI=postgres://postgres:<password>@127.0.0.1:5432/your-database-name
 | 
					DATABASE_URI=postgres://postgres:<password>@127.0.0.1:5432/your-database-name
 | 
				
			||||||
PAYLOAD_SECRET=YOUR_SECRET_HERE
 | 
					PAYLOAD_SECRET=YOUR_SECRET_HERE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PORT=9889
 | 
					DOMIAN_NAME=
 | 
				
			||||||
 | 
					 | 
				
			||||||
DOMAIN_NAME=localhost:3000
 | 
					 | 
				
			||||||
SERVER_URL=http://localhost:3000
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
SMTP_HOST=
 | 
					SMTP_HOST=
 | 
				
			||||||
SMTP_USER=
 | 
					SMTP_USER=
 | 
				
			||||||
SMTP_PASS=
 | 
					SMTP_PASS=
 | 
				
			||||||
SMTP_PORT=587
 | 
					SMTP_PORT=587
 | 
				
			||||||
PASSWORD_RESET_EXPIRATION_IN_MINUTES=
 | 
					PASSWORD_RESET_EXPIRATION_IN_MINUTES=
 | 
				
			||||||
ENV NEXT_TELEMETRY_DISABLED 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -28,7 +28,7 @@ COPY . .
 | 
				
			|||||||
# Next.js collects completely anonymous telemetry data about general usage.
 | 
					# Next.js collects completely anonymous telemetry data about general usage.
 | 
				
			||||||
# Learn more here: https://nextjs.org/telemetry
 | 
					# Learn more here: https://nextjs.org/telemetry
 | 
				
			||||||
# Uncomment the following line in case you want to disable telemetry during the build.
 | 
					# Uncomment the following line in case you want to disable telemetry during the build.
 | 
				
			||||||
 ENV NEXT_TELEMETRY_DISABLED 1
 | 
					# ENV NEXT_TELEMETRY_DISABLED 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN \
 | 
					RUN \
 | 
				
			||||||
  if [ -f yarn.lock ]; then yarn run build; \
 | 
					  if [ -f yarn.lock ]; then yarn run build; \
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,43 @@
 | 
				
			|||||||
 | 
					version: '3'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
services:
 | 
					services:
 | 
				
			||||||
  payload:
 | 
					  payload:
 | 
				
			||||||
    container_name: "midrashim"
 | 
					 | 
				
			||||||
    image: node:18-alpine
 | 
					    image: node:18-alpine
 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - '${PORT}:${PORT}'
 | 
					      - '3000:3000'
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - .:/home/node/app
 | 
					      - .:/home/node/app
 | 
				
			||||||
      - node_modules:/home/node/app/node_modules
 | 
					      - node_modules:/home/node/app/node_modules
 | 
				
			||||||
    working_dir: /home/node/app/
 | 
					    working_dir: /home/node/app/
 | 
				
			||||||
    command: sh -c "npm install && npm run build && npm start"
 | 
					    command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
 | 
				
			||||||
 | 
					    depends_on:
 | 
				
			||||||
 | 
					      - mongo
 | 
				
			||||||
 | 
					      # - postgres
 | 
				
			||||||
    env_file:
 | 
					    env_file:
 | 
				
			||||||
      - .env
 | 
					      - .env
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Ensure your DATABASE_URI uses 'mongo' as the hostname ie. mongodb://mongo/my-db-name
 | 
				
			||||||
 | 
					  mongo:
 | 
				
			||||||
 | 
					    image: mongo:latest
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - '27017:27017'
 | 
				
			||||||
 | 
					    command:
 | 
				
			||||||
 | 
					      - --storageEngine=wiredTiger
 | 
				
			||||||
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - data:/data/db
 | 
				
			||||||
 | 
					    logging:
 | 
				
			||||||
 | 
					      driver: none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Uncomment the following to use postgres
 | 
				
			||||||
 | 
					  # postgres:
 | 
				
			||||||
 | 
					  #   restart: always
 | 
				
			||||||
 | 
					  #   image: postgres:latest
 | 
				
			||||||
 | 
					  #   volumes:
 | 
				
			||||||
 | 
					  #     - pgdata:/var/lib/postgresql/data
 | 
				
			||||||
 | 
					  #   ports:
 | 
				
			||||||
 | 
					  #     - "5432:5432"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
volumes:
 | 
					volumes:
 | 
				
			||||||
  data:
 | 
					  data:
 | 
				
			||||||
 | 
					  # pgdata:
 | 
				
			||||||
  node_modules:
 | 
					  node_modules:
 | 
				
			||||||
 | 
				
			|||||||
@ -11,10 +11,6 @@ const nextConfig = {
 | 
				
			|||||||
        source: '/search',
 | 
					        source: '/search',
 | 
				
			||||||
        destination: '/books',
 | 
					        destination: '/books',
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        source: '/profile',
 | 
					 | 
				
			||||||
        destination: '/manage',
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -12,7 +12,6 @@
 | 
				
			|||||||
        "@headlessui/react": "^2.2.1",
 | 
					        "@headlessui/react": "^2.2.1",
 | 
				
			||||||
        "@heroicons/react": "^2.2.0",
 | 
					        "@heroicons/react": "^2.2.0",
 | 
				
			||||||
        "@hookform/resolvers": "^5.0.1",
 | 
					        "@hookform/resolvers": "^5.0.1",
 | 
				
			||||||
        "@next/env": "^15.3.1",
 | 
					 | 
				
			||||||
        "@payloadcms/db-postgres": "3.31.0",
 | 
					        "@payloadcms/db-postgres": "3.31.0",
 | 
				
			||||||
        "@payloadcms/next": "3.31.0",
 | 
					        "@payloadcms/next": "3.31.0",
 | 
				
			||||||
        "@payloadcms/payload-cloud": "3.31.0",
 | 
					        "@payloadcms/payload-cloud": "3.31.0",
 | 
				
			||||||
@ -3338,9 +3337,9 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@next/env": {
 | 
					    "node_modules/@next/env": {
 | 
				
			||||||
      "version": "15.3.1",
 | 
					      "version": "15.2.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.3.tgz",
 | 
				
			||||||
      "integrity": "sha512-cwK27QdzrMblHSn9DZRV+DQscHXRuJv6MydlJRpFSqJWZrTYMLzKDeyueJNN9MGd8NNiUKzDQADAf+dMLXX7YQ==",
 | 
					      "integrity": "sha512-a26KnbW9DFEUsSxAxKBORR/uD9THoYoKbkpFywMN/AFvboTt94b8+g/07T8J6ACsdLag8/PDU60ov4rPxRAixw==",
 | 
				
			||||||
      "license": "MIT"
 | 
					      "license": "MIT"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@next/eslint-plugin-next": {
 | 
					    "node_modules/@next/eslint-plugin-next": {
 | 
				
			||||||
@ -12070,12 +12069,6 @@
 | 
				
			|||||||
        "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
 | 
					        "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/next/node_modules/@next/env": {
 | 
					 | 
				
			||||||
      "version": "15.2.3",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.3.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-a26KnbW9DFEUsSxAxKBORR/uD9THoYoKbkpFywMN/AFvboTt94b8+g/07T8J6ACsdLag8/PDU60ov4rPxRAixw==",
 | 
					 | 
				
			||||||
      "license": "MIT"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/next/node_modules/postcss": {
 | 
					    "node_modules/next/node_modules/postcss": {
 | 
				
			||||||
      "version": "8.4.31",
 | 
					      "version": "8.4.31",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,6 @@
 | 
				
			|||||||
    "@headlessui/react": "^2.2.1",
 | 
					    "@headlessui/react": "^2.2.1",
 | 
				
			||||||
    "@heroicons/react": "^2.2.0",
 | 
					    "@heroicons/react": "^2.2.0",
 | 
				
			||||||
    "@hookform/resolvers": "^5.0.1",
 | 
					    "@hookform/resolvers": "^5.0.1",
 | 
				
			||||||
    "@next/env": "^15.3.1",
 | 
					 | 
				
			||||||
    "@payloadcms/db-postgres": "3.31.0",
 | 
					    "@payloadcms/db-postgres": "3.31.0",
 | 
				
			||||||
    "@payloadcms/next": "3.31.0",
 | 
					    "@payloadcms/next": "3.31.0",
 | 
				
			||||||
    "@payloadcms/payload-cloud": "3.31.0",
 | 
					    "@payloadcms/payload-cloud": "3.31.0",
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,5 @@
 | 
				
			|||||||
import SiteNavigation from '@/components/SiteNavigation'
 | 
					import SiteNavigation from '@/components/SiteNavigation'
 | 
				
			||||||
import React from 'react'
 | 
					import React from 'react'
 | 
				
			||||||
import { getPayload } from 'payload'
 | 
					 | 
				
			||||||
import config from '@/payload.config'
 | 
					 | 
				
			||||||
import { Header } from '@/payload-types'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const metadata = {
 | 
					export const metadata = {
 | 
				
			||||||
  description: 'House of Study for Temple Beth El',
 | 
					  description: 'House of Study for Temple Beth El',
 | 
				
			||||||
@ -12,19 +9,5 @@ export const metadata = {
 | 
				
			|||||||
export default async function RootLayout(props: { children: React.ReactNode }) {
 | 
					export default async function RootLayout(props: { children: React.ReactNode }) {
 | 
				
			||||||
  const { children } = props
 | 
					  const { children } = props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const payloadConfig = await config
 | 
					  return <SiteNavigation>{children}</SiteNavigation>
 | 
				
			||||||
  const payload = await getPayload({ config: payloadConfig })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const headerGlobals = (await payload.findGlobal({
 | 
					 | 
				
			||||||
    slug: 'header',
 | 
					 | 
				
			||||||
  })) as Header
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const navItems =
 | 
					 | 
				
			||||||
    headerGlobals.headerLinks?.map((l) => ({
 | 
					 | 
				
			||||||
      label: l.label || '',
 | 
					 | 
				
			||||||
      href: l.href || '',
 | 
					 | 
				
			||||||
      newTab: l.newTab || false,
 | 
					 | 
				
			||||||
    })) || []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return <SiteNavigation navItems={navItems}>{children}</SiteNavigation>
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,18 +0,0 @@
 | 
				
			|||||||
'use client'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import Manage from '@/components/Manage/Manage'
 | 
					 | 
				
			||||||
import { Checkout, Repository, User } from '@/payload-types'
 | 
					 | 
				
			||||||
import { PaginatedDocs } from 'payload'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Props = {
 | 
					 | 
				
			||||||
  repos: PaginatedDocs<Repository> | null
 | 
					 | 
				
			||||||
  borrows: PaginatedDocs<Checkout> | null
 | 
					 | 
				
			||||||
  loans: PaginatedDocs<Checkout> | null
 | 
					 | 
				
			||||||
  user: User | null
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
const ManagePageClient = (props: Props) => {
 | 
					 | 
				
			||||||
  const { repos, borrows, loans, user } = props
 | 
					 | 
				
			||||||
  return <Manage repos={repos} borrows={borrows} user={user} loans={loans} />
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default ManagePageClient
 | 
					 | 
				
			||||||
@ -1,119 +0,0 @@
 | 
				
			|||||||
import { headers as getHeaders } from 'next/headers.js'
 | 
					 | 
				
			||||||
import { getPayload, PaginatedDocs } from 'payload'
 | 
					 | 
				
			||||||
import { Checkout, Repository } from '@/payload-types'
 | 
					 | 
				
			||||||
import config from '@/payload.config'
 | 
					 | 
				
			||||||
import ManagePageClient from './page.client'
 | 
					 | 
				
			||||||
import { PageBreadCrumb, Route } from '@/components/PageBreadCrumb'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const breadcrumRoutes: Route[] = [
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    label: 'Home',
 | 
					 | 
				
			||||||
    href: '/',
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    label: 'Manage',
 | 
					 | 
				
			||||||
    href: '/manage',
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const ManagePage = async () => {
 | 
					 | 
				
			||||||
  const headers = await getHeaders()
 | 
					 | 
				
			||||||
  const payloadConfig = await config
 | 
					 | 
				
			||||||
  const payload = await getPayload({ config: payloadConfig })
 | 
					 | 
				
			||||||
  const { user } = await payload.auth({ headers })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let userRepos: PaginatedDocs<Repository> | null = null
 | 
					 | 
				
			||||||
  if (user?.id)
 | 
					 | 
				
			||||||
    userRepos = (await payload.find({
 | 
					 | 
				
			||||||
      collection: 'repositories',
 | 
					 | 
				
			||||||
      depth: 3,
 | 
					 | 
				
			||||||
      limit: 10,
 | 
					 | 
				
			||||||
      select: {
 | 
					 | 
				
			||||||
        name: true,
 | 
					 | 
				
			||||||
        abbreviation: true,
 | 
					 | 
				
			||||||
        image: true,
 | 
					 | 
				
			||||||
        description: true,
 | 
					 | 
				
			||||||
        dateOpenToPublic: true,
 | 
					 | 
				
			||||||
        holdRequests: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      where: {
 | 
					 | 
				
			||||||
        'owner.id': {
 | 
					 | 
				
			||||||
          equals: user.id,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      joins: {
 | 
					 | 
				
			||||||
        holdRequests: {
 | 
					 | 
				
			||||||
          limit: 100,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    })) as PaginatedDocs<Repository>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let userBorrows: PaginatedDocs<Checkout> | null = null
 | 
					 | 
				
			||||||
  if (user?.id)
 | 
					 | 
				
			||||||
    userBorrows = await payload.find({
 | 
					 | 
				
			||||||
      collection: 'checkouts',
 | 
					 | 
				
			||||||
      depth: 3,
 | 
					 | 
				
			||||||
      limit: 10,
 | 
					 | 
				
			||||||
      select: {
 | 
					 | 
				
			||||||
        id: true,
 | 
					 | 
				
			||||||
        copy: true,
 | 
					 | 
				
			||||||
        dateDue: true,
 | 
					 | 
				
			||||||
        ownerVerifiedReturnedDate: true,
 | 
					 | 
				
			||||||
        loaneeReturnedDate: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      sort: 'dateDue',
 | 
					 | 
				
			||||||
      where: {
 | 
					 | 
				
			||||||
        and: [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            isReturned: {
 | 
					 | 
				
			||||||
              not_equals: true,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            'user.id': {
 | 
					 | 
				
			||||||
              equals: user.id,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let userLoans: PaginatedDocs<Checkout> | null = null
 | 
					 | 
				
			||||||
  if (user?.id)
 | 
					 | 
				
			||||||
    userLoans = await payload.find({
 | 
					 | 
				
			||||||
      collection: 'checkouts',
 | 
					 | 
				
			||||||
      depth: 2,
 | 
					 | 
				
			||||||
      limit: 10,
 | 
					 | 
				
			||||||
      select: {
 | 
					 | 
				
			||||||
        id: true,
 | 
					 | 
				
			||||||
        copy: true,
 | 
					 | 
				
			||||||
        dateDue: true,
 | 
					 | 
				
			||||||
        ownerVerifiedReturnedDate: true,
 | 
					 | 
				
			||||||
        loaneeReturnedDate: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      sort: 'dateDue',
 | 
					 | 
				
			||||||
      where: {
 | 
					 | 
				
			||||||
        and: [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            isReturned: {
 | 
					 | 
				
			||||||
              not_equals: true,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            'copy.repository.owner.id': {
 | 
					 | 
				
			||||||
              equals: user.id,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <>
 | 
					 | 
				
			||||||
      <PageBreadCrumb routes={breadcrumRoutes} />
 | 
					 | 
				
			||||||
      <ManagePageClient repos={userRepos} borrows={userBorrows} user={user} loans={userLoans} />
 | 
					 | 
				
			||||||
    </>
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default ManagePage
 | 
					 | 
				
			||||||
@ -59,9 +59,9 @@ export default async function HomePage() {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
    })) as PaginatedDocs<Repository>
 | 
					    })) as PaginatedDocs<Repository>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let userBorrows: PaginatedDocs<Checkout> | null = null
 | 
					  let userCheckouts: PaginatedDocs<Checkout> | null = null
 | 
				
			||||||
  if (user?.id)
 | 
					  if (user?.id)
 | 
				
			||||||
    userBorrows = await payload.find({
 | 
					    userCheckouts = await payload.find({
 | 
				
			||||||
      collection: 'checkouts',
 | 
					      collection: 'checkouts',
 | 
				
			||||||
      depth: 3,
 | 
					      depth: 3,
 | 
				
			||||||
      limit: 10,
 | 
					      limit: 10,
 | 
				
			||||||
@ -69,8 +69,6 @@ export default async function HomePage() {
 | 
				
			|||||||
        id: true,
 | 
					        id: true,
 | 
				
			||||||
        copy: true,
 | 
					        copy: true,
 | 
				
			||||||
        dateDue: true,
 | 
					        dateDue: true,
 | 
				
			||||||
        ownerVerifiedReturnedDate: true,
 | 
					 | 
				
			||||||
        loaneeReturnedDate: true,
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      sort: 'dateDue',
 | 
					      sort: 'dateDue',
 | 
				
			||||||
      where: {
 | 
					      where: {
 | 
				
			||||||
@ -89,36 +87,6 @@ export default async function HomePage() {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let userLoans: PaginatedDocs<Checkout> | null = null
 | 
					 | 
				
			||||||
  if (user?.id)
 | 
					 | 
				
			||||||
    userLoans = await payload.find({
 | 
					 | 
				
			||||||
      collection: 'checkouts',
 | 
					 | 
				
			||||||
      depth: 2,
 | 
					 | 
				
			||||||
      limit: 10,
 | 
					 | 
				
			||||||
      select: {
 | 
					 | 
				
			||||||
        id: true,
 | 
					 | 
				
			||||||
        copy: true,
 | 
					 | 
				
			||||||
        dateDue: true,
 | 
					 | 
				
			||||||
        ownerVerifiedReturnedDate: true,
 | 
					 | 
				
			||||||
        loaneeReturnedDate: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      sort: 'dateDue',
 | 
					 | 
				
			||||||
      where: {
 | 
					 | 
				
			||||||
        and: [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            isReturned: {
 | 
					 | 
				
			||||||
              not_equals: true,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            'copy.repository.owner.id': {
 | 
					 | 
				
			||||||
              equals: user.id,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className="home">
 | 
					    <div className="home">
 | 
				
			||||||
      <HomeHero user={user} />
 | 
					      <HomeHero user={user} />
 | 
				
			||||||
@ -140,7 +108,7 @@ export default async function HomePage() {
 | 
				
			|||||||
            </TabsContent>
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TabsContent value="manage">
 | 
					            <TabsContent value="manage">
 | 
				
			||||||
              <Manage repos={userRepos} user={user} borrows={userBorrows} loans={userLoans} />
 | 
					              <Manage repos={userRepos} user={user} checkouts={userCheckouts} />
 | 
				
			||||||
            </TabsContent>
 | 
					            </TabsContent>
 | 
				
			||||||
          </Tabs>
 | 
					          </Tabs>
 | 
				
			||||||
        ) : (
 | 
					        ) : (
 | 
				
			||||||
 | 
				
			|||||||
@ -42,7 +42,7 @@ const Checkouts: CollectionConfig = {
 | 
				
			|||||||
      type: 'date',
 | 
					      type: 'date',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'loaneeReturnedDate',
 | 
					      name: 'loanerReturnedDate',
 | 
				
			||||||
      type: 'date',
 | 
					      type: 'date',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,3 @@
 | 
				
			|||||||
import { authenticated } from "@/access/authenticated";
 | 
					 | 
				
			||||||
import { Copy } from "@/payload-types";
 | 
					import { Copy } from "@/payload-types";
 | 
				
			||||||
import { CollectionBeforeValidateHook, CollectionConfig } from "payload";
 | 
					import { CollectionBeforeValidateHook, CollectionConfig } from "payload";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -39,22 +38,12 @@ export const Copies: CollectionConfig = {
 | 
				
			|||||||
      limits: [10, 20],
 | 
					      limits: [10, 20],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  access: {
 | 
					 | 
				
			||||||
    read: () => true,
 | 
					 | 
				
			||||||
    update: authenticated,
 | 
					 | 
				
			||||||
    create: authenticated,
 | 
					 | 
				
			||||||
    delete: authenticated,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  fields: [
 | 
					  fields: [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'label',
 | 
					      name: 'label',
 | 
				
			||||||
      type: 'text',
 | 
					      type: 'text',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      name: 'isOpenToPublic',
 | 
					 | 
				
			||||||
      type: 'checkbox',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'condition',
 | 
					      name: 'condition',
 | 
				
			||||||
      label: 'Condition out of 5',
 | 
					      label: 'Condition out of 5',
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,8 @@
 | 
				
			|||||||
import { admin } from '@/access/admin'
 | 
					import { admin } from '@/access/admin'
 | 
				
			||||||
import type { CollectionConfig } from 'payload'
 | 
					import type { CollectionConfig } from 'payload'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import '../envConfig'
 | 
					const expirationInMinutes = parseInt(process.env.PASSWORD_RESET_EXPIRATION_IN_MINUTES || '30')
 | 
				
			||||||
 | 
					const domain = process.env.DOMAIN_NAME || 'localhost:3000'
 | 
				
			||||||
const expirationInMinutes = parseInt(process.env.PASSWORD_RESET_EXPIRATION_IN_MINUTES || '30', 10)
 | 
					 | 
				
			||||||
const domain = process.env.DOMAIN_NAME || ''
 | 
					 | 
				
			||||||
const serverUrl = process.env.SERVER_URL || ''
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Users: CollectionConfig = {
 | 
					export const Users: CollectionConfig = {
 | 
				
			||||||
  slug: 'users',
 | 
					  slug: 'users',
 | 
				
			||||||
@ -16,13 +13,31 @@ export const Users: CollectionConfig = {
 | 
				
			|||||||
    admin: admin
 | 
					    admin: admin
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  auth: {
 | 
					  auth: {
 | 
				
			||||||
 | 
					    //   verify: {
 | 
				
			||||||
 | 
					    //     generateEmailSubject: () => {
 | 
				
			||||||
 | 
					    //       return `Verify Account for ${domain}`
 | 
				
			||||||
 | 
					    //     },
 | 
				
			||||||
 | 
					    //     generateEmailHTML: ({ req, token, user }) => {
 | 
				
			||||||
 | 
					    //       const url = `https://${domain}/verify?token=${token}`
 | 
				
			||||||
 | 
					    //       return `
 | 
				
			||||||
 | 
					    //         <!doctype html>
 | 
				
			||||||
 | 
					    //         <html>
 | 
				
			||||||
 | 
					    //           <body>
 | 
				
			||||||
 | 
					    //             <h1>Verify Account for ${domain}</h1>
 | 
				
			||||||
 | 
					    //             <p>Hey ${user.email}, verify your email by clicking here: ${url}</p>
 | 
				
			||||||
 | 
					    //             <p>If you have not recently been signed up for ${domain} then please ignore this email.</p>
 | 
				
			||||||
 | 
					    //           </body>
 | 
				
			||||||
 | 
					    //         </html>
 | 
				
			||||||
 | 
					    //       `
 | 
				
			||||||
 | 
					    //     },
 | 
				
			||||||
 | 
					    //   },
 | 
				
			||||||
    forgotPassword: {
 | 
					    forgotPassword: {
 | 
				
			||||||
 | 
					      expiration: (60000 * expirationInMinutes),
 | 
				
			||||||
      generateEmailSubject: () => {
 | 
					      generateEmailSubject: () => {
 | 
				
			||||||
        console.log({ domain, serverUrl, expirationInMinutes })
 | 
					 | 
				
			||||||
        return `Reset password request for ${domain}`
 | 
					        return `Reset password request for ${domain}`
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      generateEmailHTML: (props) => {
 | 
					      generateEmailHTML: (props) => {
 | 
				
			||||||
        const resetPasswordURL = `${serverUrl}/forgotPassword?token=${props?.token}`
 | 
					        const resetPasswordURL = `https://${domain}/forgotPassword?token=${props?.token}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return `
 | 
					        return `
 | 
				
			||||||
          <!doctype html>
 | 
					          <!doctype html>
 | 
				
			||||||
@ -43,6 +58,7 @@ export const Users: CollectionConfig = {
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  fields: [
 | 
					  fields: [
 | 
				
			||||||
    // Email added by default
 | 
					    // Email added by default
 | 
				
			||||||
 | 
					    // Add more fields as needed
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'role',
 | 
					      name: 'role',
 | 
				
			||||||
      type: 'select',
 | 
					      type: 'select',
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ const UserFeed = async (props: Props) => {
 | 
				
			|||||||
  const payloadConfig = await config
 | 
					  const payloadConfig = await config
 | 
				
			||||||
  const payload = await getPayload({ config: payloadConfig })
 | 
					  const payload = await getPayload({ config: payloadConfig })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const borrowRequests = (await payload.find({
 | 
					  const holdRequests = (await payload.find({
 | 
				
			||||||
    collection: 'holdRequests',
 | 
					    collection: 'holdRequests',
 | 
				
			||||||
    limit: 10,
 | 
					    limit: 10,
 | 
				
			||||||
    depth: 3,
 | 
					    depth: 3,
 | 
				
			||||||
@ -97,13 +97,7 @@ const UserFeed = async (props: Props) => {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
    })) as PaginatedDocs<Checkout>
 | 
					    })) as PaginatedDocs<Checkout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const totalHoldNotifications =
 | 
					  const totalHoldNotifications = repos?.docs.flatMap((r) => r.holdRequests?.docs).length || 0
 | 
				
			||||||
    repos?.docs
 | 
					 | 
				
			||||||
      .flatMap((r) => r.holdRequests?.docs)
 | 
					 | 
				
			||||||
      .filter((r) => {
 | 
					 | 
				
			||||||
        const repo = r as HoldRequest
 | 
					 | 
				
			||||||
        return !repo.isRejected && !repo.isCheckedOut
 | 
					 | 
				
			||||||
      }).length || 0
 | 
					 | 
				
			||||||
  const outBoundLoanCount = loanedOutBooks?.totalDocs || 0
 | 
					  const outBoundLoanCount = loanedOutBooks?.totalDocs || 0
 | 
				
			||||||
  const currentlyHoldingCount = currentlyHeldBooks?.totalDocs || 0
 | 
					  const currentlyHoldingCount = currentlyHeldBooks?.totalDocs || 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -112,7 +106,7 @@ const UserFeed = async (props: Props) => {
 | 
				
			|||||||
      name: 'Hold Request',
 | 
					      name: 'Hold Request',
 | 
				
			||||||
      iconSrc: '/images/mail.svg',
 | 
					      iconSrc: '/images/mail.svg',
 | 
				
			||||||
      value: totalHoldNotifications,
 | 
					      value: totalHoldNotifications,
 | 
				
			||||||
      href: '/manage',
 | 
					      href: '#',
 | 
				
			||||||
      ctaText: 'Answer Requests',
 | 
					      ctaText: 'Answer Requests',
 | 
				
			||||||
      shouldAnimate: totalHoldNotifications > 0,
 | 
					      shouldAnimate: totalHoldNotifications > 0,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -120,7 +114,7 @@ const UserFeed = async (props: Props) => {
 | 
				
			|||||||
      name: 'Loaned Out',
 | 
					      name: 'Loaned Out',
 | 
				
			||||||
      iconSrc: '/images/book-loan.svg',
 | 
					      iconSrc: '/images/book-loan.svg',
 | 
				
			||||||
      value: outBoundLoanCount,
 | 
					      value: outBoundLoanCount,
 | 
				
			||||||
      href: '/manage',
 | 
					      href: '#',
 | 
				
			||||||
      ctaText: 'Handle Returns',
 | 
					      ctaText: 'Handle Returns',
 | 
				
			||||||
      shouldAnimate: false,
 | 
					      shouldAnimate: false,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -128,7 +122,7 @@ const UserFeed = async (props: Props) => {
 | 
				
			|||||||
      name: 'Currently Holding',
 | 
					      name: 'Currently Holding',
 | 
				
			||||||
      iconSrc: '/images/book-shelf.svg',
 | 
					      iconSrc: '/images/book-shelf.svg',
 | 
				
			||||||
      value: currentlyHoldingCount,
 | 
					      value: currentlyHoldingCount,
 | 
				
			||||||
      href: '/manage',
 | 
					      href: '#',
 | 
				
			||||||
      ctaText: 'Loan Out',
 | 
					      ctaText: 'Loan Out',
 | 
				
			||||||
      shouldAnimate: false,
 | 
					      shouldAnimate: false,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -137,7 +131,7 @@ const UserFeed = async (props: Props) => {
 | 
				
			|||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <section>
 | 
					    <section>
 | 
				
			||||||
      <div className="my-6">
 | 
					      <div className="my-6">
 | 
				
			||||||
        <h3 className="text-lg font-semibold text-foreground">Loan Activity</h3>
 | 
					        <h3 className="text-lg font-semibold text-foreground">Outbound Activity</h3>
 | 
				
			||||||
        <dl className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
 | 
					        <dl className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
 | 
				
			||||||
          {stats.map((s) => {
 | 
					          {stats.map((s) => {
 | 
				
			||||||
            return (
 | 
					            return (
 | 
				
			||||||
@ -182,11 +176,11 @@ const UserFeed = async (props: Props) => {
 | 
				
			|||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div className="my-6">
 | 
					      <div className="my-6">
 | 
				
			||||||
        <h2 className="text-lg font-semibold text-foreground">Borrow Activity</h2>
 | 
					        <h2 className="text-lg font-semibold text-foreground">Inbound Activity</h2>
 | 
				
			||||||
        <div className="my-3">
 | 
					        <div className="my-3">
 | 
				
			||||||
          <h3 className="text-base font-semibold text-muted-foreground mb-4">Your Holds</h3>
 | 
					          <h3 className="text-base font-semibold text-muted-foreground mb-4">Your Holds</h3>
 | 
				
			||||||
          <ul className="grid grid-cols-1 gap-y-6 sm:grid-cols-3 md:grid-cols-4 last-child-adjustment">
 | 
					          <ul className="grid grid-cols-1 gap-y-6 sm:grid-cols-3 md:grid-cols-4 last-child-adjustment">
 | 
				
			||||||
            {borrowRequests.docs?.map((h) => {
 | 
					            {holdRequests.docs?.map((h) => {
 | 
				
			||||||
              const book = h.book as Book
 | 
					              const book = h.book as Book
 | 
				
			||||||
              const repository = h.repository as Repository
 | 
					              const repository = h.repository as Repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,228 +0,0 @@
 | 
				
			|||||||
'use client'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { Author, Book, Checkout, Copy, Repository } from '@/payload-types'
 | 
					 | 
				
			||||||
import { useGlobal } from '@/providers/GlobalProvider'
 | 
					 | 
				
			||||||
import { getUserBorrows } from '@/serverActions/GetUserBorrows'
 | 
					 | 
				
			||||||
import { loaneeReturnCheckout } from '@/serverActions/ReturnCheckout'
 | 
					 | 
				
			||||||
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
 | 
					 | 
				
			||||||
import { EllipsisVerticalIcon } from '@heroicons/react/20/solid'
 | 
					 | 
				
			||||||
import clsx from 'clsx'
 | 
					 | 
				
			||||||
import { Loader2Icon } from 'lucide-react'
 | 
					 | 
				
			||||||
import { PaginatedDocs, User } from 'payload'
 | 
					 | 
				
			||||||
import { useCallback, useMemo, useState } from 'react'
 | 
					 | 
				
			||||||
import { toast } from 'sonner'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const statuses = {
 | 
					 | 
				
			||||||
  'Passed Due': 'text-gray-200 bg-red-500 ring-red-700/10',
 | 
					 | 
				
			||||||
  'Due Soon': 'text-amber-800 bg-amber-50 ring-amber-600/20',
 | 
					 | 
				
			||||||
  'Owner Action': 'text-emerald-800 bg-emerald-50 ring-emerald-600/20',
 | 
					 | 
				
			||||||
  '': '',
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Row = {
 | 
					 | 
				
			||||||
  id: number
 | 
					 | 
				
			||||||
  title: string
 | 
					 | 
				
			||||||
  authors: string[]
 | 
					 | 
				
			||||||
  owners: string[]
 | 
					 | 
				
			||||||
  repositoryAbbreviation: string
 | 
					 | 
				
			||||||
  status: 'Passed Due' | 'Due Soon' | ''
 | 
					 | 
				
			||||||
  dueDate: Date
 | 
					 | 
				
			||||||
  href: string
 | 
					 | 
				
			||||||
  doesNeedOwnerVerify: boolean
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ListProps = {
 | 
					 | 
				
			||||||
  rows: Row[]
 | 
					 | 
				
			||||||
  onUpdate: () => Promise<void>
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
const BorrowedBooksList = (props: ListProps) => {
 | 
					 | 
				
			||||||
  const { rows, onUpdate } = props
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const [returningBookId, setReturningBookId] = useState(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const isReturningBook = useCallback((id: number) => id === returningBookId, [returningBookId])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const handleReturnClick = useCallback(
 | 
					 | 
				
			||||||
    async (id: number) => {
 | 
					 | 
				
			||||||
      if (returningBookId) return
 | 
					 | 
				
			||||||
      setReturningBookId(id)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const updatedCheckout = await loaneeReturnCheckout({ checkoutId: id })
 | 
					 | 
				
			||||||
      if (!updatedCheckout) {
 | 
					 | 
				
			||||||
        setReturningBookId(0)
 | 
					 | 
				
			||||||
        toast('There was an issue returning your book')
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await onUpdate()
 | 
					 | 
				
			||||||
      setReturningBookId(0)
 | 
					 | 
				
			||||||
      toast('Book was returned, awaiting owner verification.')
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    [returningBookId, setReturningBookId, onUpdate],
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <ul role="list" className="divide-y divide-gray-100">
 | 
					 | 
				
			||||||
      {rows.map((r) => (
 | 
					 | 
				
			||||||
        <li
 | 
					 | 
				
			||||||
          key={r.id}
 | 
					 | 
				
			||||||
          className={clsx(
 | 
					 | 
				
			||||||
            'flex relative items-center justify-between gap-x-6 py-5',
 | 
					 | 
				
			||||||
            isReturningBook(r.id) ? 'pointer-events-none opacity-30' : '',
 | 
					 | 
				
			||||||
            r.doesNeedOwnerVerify ? 'pointer-events-none opacity-30' : '',
 | 
					 | 
				
			||||||
          )}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <div className="min-w-0">
 | 
					 | 
				
			||||||
            <div className="flex items-start gap-x-3">
 | 
					 | 
				
			||||||
              <p className="text-sm/6 text-foreground">
 | 
					 | 
				
			||||||
                <span className="font-semibold">{`[${r.repositoryAbbreviation}] `}</span>
 | 
					 | 
				
			||||||
                <span>{r.title}</span>
 | 
					 | 
				
			||||||
              </p>
 | 
					 | 
				
			||||||
              {!!r.status && (
 | 
					 | 
				
			||||||
                <p
 | 
					 | 
				
			||||||
                  className={clsx(
 | 
					 | 
				
			||||||
                    statuses[r.status as keyof typeof statuses],
 | 
					 | 
				
			||||||
                    'mt-0.5 rounded-md px-1.5 py-0.5 text-xs font-medium whitespace-nowrap ring-1 ring-inset',
 | 
					 | 
				
			||||||
                  )}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                  {r.status}
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
              )}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div className="mt-1 flex items-center gap-x-2 text-xs/5 text-gray-500">
 | 
					 | 
				
			||||||
              <p className="whitespace-nowrap">
 | 
					 | 
				
			||||||
                {r.doesNeedOwnerVerify ? (
 | 
					 | 
				
			||||||
                  <span>Awaiting Owner Verification</span>
 | 
					 | 
				
			||||||
                ) : (
 | 
					 | 
				
			||||||
                  <span>
 | 
					 | 
				
			||||||
                    Due on{' '}
 | 
					 | 
				
			||||||
                    <time dateTime={r.dueDate.toString()}>{r.dueDate.toLocaleDateString()}</time>
 | 
					 | 
				
			||||||
                  </span>
 | 
					 | 
				
			||||||
                )}
 | 
					 | 
				
			||||||
              </p>
 | 
					 | 
				
			||||||
              <svg viewBox="0 0 2 2" className="size-0.5 fill-current">
 | 
					 | 
				
			||||||
                <circle r={1} cx={1} cy={1} />
 | 
					 | 
				
			||||||
              </svg>
 | 
					 | 
				
			||||||
              <p className="truncate">Borrowed from {r.owners.join(', ')}</p>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div className="flex flex-none items-center gap-x-4">
 | 
					 | 
				
			||||||
            <button
 | 
					 | 
				
			||||||
              type="button"
 | 
					 | 
				
			||||||
              disabled={!!returningBookId}
 | 
					 | 
				
			||||||
              className="hidden disabled:opacity-20 cursor-pointer disabled:cursor-auto rounded-md bg-background px-2.5 py-1.5 text-sm font-semibold text-muted-foreground shadow-xs ring-1 ring-gray-300 ring-inset hover:bg-gray-50 sm:flex items-center gap-1"
 | 
					 | 
				
			||||||
              onClick={() => handleReturnClick(r.id)}
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              {isReturningBook(r.id) && <Loader2Icon className="animate-spin" />}
 | 
					 | 
				
			||||||
              <span>Return</span>
 | 
					 | 
				
			||||||
            </button>
 | 
					 | 
				
			||||||
            <Menu as="div" className="relative flex-none">
 | 
					 | 
				
			||||||
              <MenuButton className="-m-2.5 block p-2.5 text-gray-500 hover:text-gray-900">
 | 
					 | 
				
			||||||
                <span className="sr-only">Open options</span>
 | 
					 | 
				
			||||||
                <EllipsisVerticalIcon aria-hidden="true" className="size-5" />
 | 
					 | 
				
			||||||
              </MenuButton>
 | 
					 | 
				
			||||||
              <MenuItems
 | 
					 | 
				
			||||||
                transition
 | 
					 | 
				
			||||||
                className="absolute right-0 z-10 mt-2 w-32 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-gray-900/5 transition focus:outline-hidden data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in"
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                <MenuItem>
 | 
					 | 
				
			||||||
                  <button
 | 
					 | 
				
			||||||
                    className="block w-full text-left cursor-pointer px-3 py-1 text-sm/6 text-gray-900 data-focus:bg-gray-50 data-focus:outline-hidden"
 | 
					 | 
				
			||||||
                    type="button"
 | 
					 | 
				
			||||||
                    onClick={() => handleReturnClick(r.id)}
 | 
					 | 
				
			||||||
                  >
 | 
					 | 
				
			||||||
                    Return<span className="sr-only">, {r.title}</span>
 | 
					 | 
				
			||||||
                  </button>
 | 
					 | 
				
			||||||
                </MenuItem>
 | 
					 | 
				
			||||||
                {/*<MenuItem>
 | 
					 | 
				
			||||||
                  <a
 | 
					 | 
				
			||||||
                    href={`/repositories/${r.repositoryAbbreviation}`}
 | 
					 | 
				
			||||||
                    className="block px-3 py-1 text-sm/6 text-gray-900 data-focus:bg-gray-50 data-focus:outline-hidden"
 | 
					 | 
				
			||||||
                  >
 | 
					 | 
				
			||||||
                    Repository<span className="sr-only">, {r.repositoryAbbreviation}</span>
 | 
					 | 
				
			||||||
                  </a>
 | 
					 | 
				
			||||||
                </MenuItem>*/}
 | 
					 | 
				
			||||||
                <MenuItem>
 | 
					 | 
				
			||||||
                  <a
 | 
					 | 
				
			||||||
                    href={r.href}
 | 
					 | 
				
			||||||
                    className="block px-3 py-1 text-sm/6 text-gray-900 data-focus:bg-gray-50 data-focus:outline-hidden"
 | 
					 | 
				
			||||||
                  >
 | 
					 | 
				
			||||||
                    View<span className="sr-only"> {r.title}</span>
 | 
					 | 
				
			||||||
                  </a>
 | 
					 | 
				
			||||||
                </MenuItem>
 | 
					 | 
				
			||||||
              </MenuItems>
 | 
					 | 
				
			||||||
            </Menu>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </li>
 | 
					 | 
				
			||||||
      ))}
 | 
					 | 
				
			||||||
    </ul>
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Props = { initialBorrows: PaginatedDocs<Checkout> | null }
 | 
					 | 
				
			||||||
const BorrowedBooks = (props: Props) => {
 | 
					 | 
				
			||||||
  const { initialBorrows } = props
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const { user } = useGlobal()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const [borrows, setBorrows] = useState<PaginatedDocs<Checkout> | null>(initialBorrows)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const onUpdate = useCallback(async () => {
 | 
					 | 
				
			||||||
    if (!user) return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const updatedUserBorrows = await getUserBorrows({ userId: user.id })
 | 
					 | 
				
			||||||
    setBorrows(updatedUserBorrows)
 | 
					 | 
				
			||||||
  }, [setBorrows, user])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const rows: Row[] = useMemo(
 | 
					 | 
				
			||||||
    () =>
 | 
					 | 
				
			||||||
      borrows?.docs?.map((c) => {
 | 
					 | 
				
			||||||
        const copy = c.copy as Copy
 | 
					 | 
				
			||||||
        const book = copy.book as Book
 | 
					 | 
				
			||||||
        const authors = book.authors as Author[]
 | 
					 | 
				
			||||||
        const repository = copy.repository as Repository
 | 
					 | 
				
			||||||
        const owners = repository.owner as (number | User)[]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const parsedDateDue = c.dateDue ? new Date(c.dateDue) : new Date()
 | 
					 | 
				
			||||||
        const milisecondsUntilDue = parsedDateDue.getTime() - new Date().getTime()
 | 
					 | 
				
			||||||
        const secondsUntilDue = Math.floor(milisecondsUntilDue / 1000)
 | 
					 | 
				
			||||||
        const minutesUntilDue = Math.floor(secondsUntilDue / 60)
 | 
					 | 
				
			||||||
        const hoursUntilDue = Math.floor(minutesUntilDue / 60)
 | 
					 | 
				
			||||||
        const daysUntilDue = Math.floor(hoursUntilDue / 24)
 | 
					 | 
				
			||||||
        const doesNeedOwnerVerify = c.loaneeReturnedDate && !c.ownerVerifiedReturnedDate
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let status = ''
 | 
					 | 
				
			||||||
        if (doesNeedOwnerVerify) status = 'Owner Action'
 | 
					 | 
				
			||||||
        else if (daysUntilDue <= 0) status = 'Passed Due'
 | 
					 | 
				
			||||||
        else if (daysUntilDue <= 5) status = 'Due Soon'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
          id: c.id || 0,
 | 
					 | 
				
			||||||
          title: book.title || '',
 | 
					 | 
				
			||||||
          authors: authors.map((a) => a.lf) || ([] as string[]),
 | 
					 | 
				
			||||||
          owners:
 | 
					 | 
				
			||||||
            owners.map((o) => `${(o as User).firstName} ${(o as User).lastName}`) ||
 | 
					 | 
				
			||||||
            ([] as string[]),
 | 
					 | 
				
			||||||
          repositoryAbbreviation: repository.abbreviation || '',
 | 
					 | 
				
			||||||
          status,
 | 
					 | 
				
			||||||
          dueDate: parsedDateDue,
 | 
					 | 
				
			||||||
          href: `/books/${book.id}`,
 | 
					 | 
				
			||||||
          doesNeedOwnerVerify,
 | 
					 | 
				
			||||||
        } as Row
 | 
					 | 
				
			||||||
      }) || [],
 | 
					 | 
				
			||||||
    [borrows],
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!rows.length) return null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <section className="py-6">
 | 
					 | 
				
			||||||
      <div className="mb-4 flex justify-between items-end">
 | 
					 | 
				
			||||||
        <h3 className="px-4 text-lg font-semibold">Borrowed Books</h3>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <BorrowedBooksList onUpdate={onUpdate} rows={rows} />
 | 
					 | 
				
			||||||
    </section>
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default BorrowedBooks
 | 
					 | 
				
			||||||
@ -1,16 +1,10 @@
 | 
				
			|||||||
'use client'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { Author, Book, Checkout, Copy, Repository } from '@/payload-types'
 | 
					import { Author, Book, Checkout, Copy, Repository } from '@/payload-types'
 | 
				
			||||||
import { useGlobal } from '@/providers/GlobalProvider'
 | 
					 | 
				
			||||||
import { getUserLoans } from '@/serverActions/GetUserLoans'
 | 
					 | 
				
			||||||
import { ownerReturnCheckout } from '@/serverActions/ReturnCheckout'
 | 
					 | 
				
			||||||
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
 | 
					import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
 | 
				
			||||||
import { EllipsisVerticalIcon } from '@heroicons/react/20/solid'
 | 
					import { EllipsisVerticalIcon } from '@heroicons/react/20/solid'
 | 
				
			||||||
import clsx from 'clsx'
 | 
					import clsx from 'clsx'
 | 
				
			||||||
import { Loader2Icon } from 'lucide-react'
 | 
					import { Loader2Icon } from 'lucide-react'
 | 
				
			||||||
import { PaginatedDocs, User } from 'payload'
 | 
					import { PaginatedDocs, User } from 'payload'
 | 
				
			||||||
import { useCallback, useMemo, useState } from 'react'
 | 
					import { useCallback, useState } from 'react'
 | 
				
			||||||
import { toast } from 'sonner'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const statuses = {
 | 
					const statuses = {
 | 
				
			||||||
  'Passed Due': 'text-gray-200 bg-red-500 ring-red-700/10',
 | 
					  'Passed Due': 'text-gray-200 bg-red-500 ring-red-700/10',
 | 
				
			||||||
@ -29,46 +23,22 @@ type Row = {
 | 
				
			|||||||
  href: string
 | 
					  href: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ListProps = {
 | 
					type ListProps = { rows: Row[] }
 | 
				
			||||||
  rows: Row[]
 | 
					const CheckedOutBooksList = (props: ListProps) => {
 | 
				
			||||||
  onUpdate: () => Promise<void>
 | 
					  const { rows } = props
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
const LoanedBooksList = (props: ListProps) => {
 | 
					 | 
				
			||||||
  const { rows, onUpdate } = props
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [returningBookId, setReturningBookId] = useState(0)
 | 
					  const [returningBookId, setReturningBookId] = useState(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const isReturningBook = useCallback((id: number) => id === returningBookId, [returningBookId])
 | 
					  const isReturningBook = useCallback((id: number) => id === returningBookId, [returningBookId])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleReturnClick = useCallback(
 | 
					  const handleReturnClick = useCallback((id: number) => {
 | 
				
			||||||
    async (id: number) => {
 | 
					 | 
				
			||||||
      if (returningBookId) return
 | 
					 | 
				
			||||||
    setReturningBookId(id)
 | 
					    setReturningBookId(id)
 | 
				
			||||||
 | 
					  }, [])
 | 
				
			||||||
      const updatedCheckout = await ownerReturnCheckout({ checkoutId: id })
 | 
					 | 
				
			||||||
      if (!updatedCheckout) {
 | 
					 | 
				
			||||||
        setReturningBookId(0)
 | 
					 | 
				
			||||||
        toast('There was an issue returning your book')
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await onUpdate()
 | 
					 | 
				
			||||||
      setReturningBookId(0)
 | 
					 | 
				
			||||||
      toast('Book was returned into your collection')
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    [returningBookId, setReturningBookId, onUpdate],
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <ul role="list" className="divide-y divide-gray-100">
 | 
					    <ul role="list" className="divide-y divide-gray-100">
 | 
				
			||||||
      {rows.map((r) => (
 | 
					      {rows.map((r) => (
 | 
				
			||||||
        <li
 | 
					        <li key={r.id} className="flex items-center justify-between gap-x-6 py-5">
 | 
				
			||||||
          key={r.id}
 | 
					 | 
				
			||||||
          className={clsx(
 | 
					 | 
				
			||||||
            'flex relative items-center justify-between gap-x-6 py-5',
 | 
					 | 
				
			||||||
            isReturningBook(r.id) ? 'pointer-events-none opacity-30' : '',
 | 
					 | 
				
			||||||
          )}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <div className="min-w-0">
 | 
					          <div className="min-w-0">
 | 
				
			||||||
            <div className="flex items-start gap-x-3">
 | 
					            <div className="flex items-start gap-x-3">
 | 
				
			||||||
              <p className="text-sm/6 text-foreground">
 | 
					              <p className="text-sm/6 text-foreground">
 | 
				
			||||||
@ -88,10 +58,7 @@ const LoanedBooksList = (props: ListProps) => {
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div className="mt-1 flex items-center gap-x-2 text-xs/5 text-gray-500">
 | 
					            <div className="mt-1 flex items-center gap-x-2 text-xs/5 text-gray-500">
 | 
				
			||||||
              <p className="whitespace-nowrap">
 | 
					              <p className="whitespace-nowrap">
 | 
				
			||||||
                <span>
 | 
					                Due on <time dateTime={r.dueDate.toString()}>{r.dueDate.toLocaleDateString()}</time>
 | 
				
			||||||
                  Due on{' '}
 | 
					 | 
				
			||||||
                  <time dateTime={r.dueDate.toString()}>{r.dueDate.toLocaleDateString()}</time>
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
              </p>
 | 
					              </p>
 | 
				
			||||||
              <svg viewBox="0 0 2 2" className="size-0.5 fill-current">
 | 
					              <svg viewBox="0 0 2 2" className="size-0.5 fill-current">
 | 
				
			||||||
                <circle r={1} cx={1} cy={1} />
 | 
					                <circle r={1} cx={1} cy={1} />
 | 
				
			||||||
@ -120,21 +87,21 @@ const LoanedBooksList = (props: ListProps) => {
 | 
				
			|||||||
              >
 | 
					              >
 | 
				
			||||||
                <MenuItem>
 | 
					                <MenuItem>
 | 
				
			||||||
                  <button
 | 
					                  <button
 | 
				
			||||||
                    className="block w-full text-left cursor-pointer px-3 py-1 text-sm/6 text-gray-900 data-focus:bg-gray-50 data-focus:outline-hidden"
 | 
					                    className="block cursor-pointer px-3 py-1 text-sm/6 text-gray-900 data-focus:bg-gray-50 data-focus:outline-hidden"
 | 
				
			||||||
                    type="button"
 | 
					                    type="button"
 | 
				
			||||||
                    onClick={() => handleReturnClick(r.id)}
 | 
					                    onClick={() => console.log('return')}
 | 
				
			||||||
                  >
 | 
					                  >
 | 
				
			||||||
                    Return<span className="sr-only">, {r.title}</span>
 | 
					                    Return<span className="sr-only">, {r.title}</span>
 | 
				
			||||||
                  </button>
 | 
					                  </button>
 | 
				
			||||||
                </MenuItem>
 | 
					                </MenuItem>
 | 
				
			||||||
                {/*<MenuItem>
 | 
					                <MenuItem>
 | 
				
			||||||
                  <a
 | 
					                  <a
 | 
				
			||||||
                    href={`/repositories/${r.repositoryAbbreviation}`}
 | 
					                    href={`/repositories/${r.repositoryAbbreviation}`}
 | 
				
			||||||
                    className="block px-3 py-1 text-sm/6 text-gray-900 data-focus:bg-gray-50 data-focus:outline-hidden"
 | 
					                    className="block px-3 py-1 text-sm/6 text-gray-900 data-focus:bg-gray-50 data-focus:outline-hidden"
 | 
				
			||||||
                  >
 | 
					                  >
 | 
				
			||||||
                    Repository<span className="sr-only">, {r.repositoryAbbreviation}</span>
 | 
					                    Repository<span className="sr-only">, {r.repositoryAbbreviation}</span>
 | 
				
			||||||
                  </a>
 | 
					                  </a>
 | 
				
			||||||
                </MenuItem>*/}
 | 
					                </MenuItem>
 | 
				
			||||||
                <MenuItem>
 | 
					                <MenuItem>
 | 
				
			||||||
                  <a
 | 
					                  <a
 | 
				
			||||||
                    href={r.href}
 | 
					                    href={r.href}
 | 
				
			||||||
@ -152,24 +119,13 @@ const LoanedBooksList = (props: ListProps) => {
 | 
				
			|||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = { initialLoans: PaginatedDocs<Checkout> | null }
 | 
					type Props = { initialCheckoutPage: PaginatedDocs<Checkout> | null }
 | 
				
			||||||
const LoanedBooks = (props: Props) => {
 | 
					const CheckedOutBooks = (props: Props) => {
 | 
				
			||||||
  const { initialLoans } = props
 | 
					  const { initialCheckoutPage } = props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { user } = useGlobal()
 | 
					  const checkouts = initialCheckoutPage?.docs
 | 
				
			||||||
 | 
					  const rows: Row[] =
 | 
				
			||||||
  const [loans, setLoans] = useState<PaginatedDocs<Checkout> | null>(initialLoans)
 | 
					    checkouts?.map((c) => {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  const onUpdate = useCallback(async () => {
 | 
					 | 
				
			||||||
    if (!user) return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const updatedUserLoans = await getUserLoans({ userId: user.id })
 | 
					 | 
				
			||||||
    setLoans(updatedUserLoans)
 | 
					 | 
				
			||||||
  }, [setLoans, user])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const rows: Row[] = useMemo(
 | 
					 | 
				
			||||||
    () =>
 | 
					 | 
				
			||||||
      loans?.docs?.map((c) => {
 | 
					 | 
				
			||||||
      const copy = c.copy as Copy
 | 
					      const copy = c.copy as Copy
 | 
				
			||||||
      const book = copy.book as Book
 | 
					      const book = copy.book as Book
 | 
				
			||||||
      const authors = book.authors as Author[]
 | 
					      const authors = book.authors as Author[]
 | 
				
			||||||
@ -192,27 +148,24 @@ const LoanedBooks = (props: Props) => {
 | 
				
			|||||||
        title: book.title || '',
 | 
					        title: book.title || '',
 | 
				
			||||||
        authors: authors.map((a) => a.lf) || ([] as string[]),
 | 
					        authors: authors.map((a) => a.lf) || ([] as string[]),
 | 
				
			||||||
        owners:
 | 
					        owners:
 | 
				
			||||||
            owners.map((o) => `${(o as User).firstName} ${(o as User).lastName}`) ||
 | 
					          owners.map((o) => `${(o as User).firstName} ${(o as User).lastName}`) || ([] as string[]),
 | 
				
			||||||
            ([] as string[]),
 | 
					 | 
				
			||||||
        repositoryAbbreviation: repository.abbreviation || '',
 | 
					        repositoryAbbreviation: repository.abbreviation || '',
 | 
				
			||||||
        status,
 | 
					        status,
 | 
				
			||||||
        dueDate: parsedDateDue,
 | 
					        dueDate: parsedDateDue,
 | 
				
			||||||
        href: `/books/${book.id}`,
 | 
					        href: `/books/${book.id}`,
 | 
				
			||||||
      } as Row
 | 
					      } as Row
 | 
				
			||||||
      }) || [],
 | 
					    }) || []
 | 
				
			||||||
    [loans],
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!rows.length) return null
 | 
					  if (!rows.length) return null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <section className="py-6">
 | 
					    <section className="py-6">
 | 
				
			||||||
      <div className="mb-4 flex justify-between items-end">
 | 
					      <div className="mb-4 flex justify-between items-end">
 | 
				
			||||||
        <h3 className="px-4 text-lg font-semibold">Loaned Out Books</h3>
 | 
					        <h3 className="px-4 text-lg font-semibold">Inbound Checked Out Books</h3>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <LoanedBooksList onUpdate={onUpdate} rows={rows} />
 | 
					      <CheckedOutBooksList rows={rows} />
 | 
				
			||||||
    </section>
 | 
					    </section>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default LoanedBooks
 | 
					export default CheckedOutBooks
 | 
				
			||||||
@ -19,13 +19,7 @@ const HoldRequestNotifications = (props: Props) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const [openedModalId, setOpenedModalId] = useState<number | null>(null)
 | 
					  const [openedModalId, setOpenedModalId] = useState<number | null>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const totalHoldNotifications =
 | 
					  const totalHoldNotifications = repos?.docs.flatMap((r) => r.holdRequests?.docs).length || 0
 | 
				
			||||||
    repos?.docs
 | 
					 | 
				
			||||||
      .flatMap((r) => r.holdRequests?.docs)
 | 
					 | 
				
			||||||
      .filter((r) => {
 | 
					 | 
				
			||||||
        const request = r as HoldRequest
 | 
					 | 
				
			||||||
        return !request.isCheckedOut && !request.isRejected
 | 
					 | 
				
			||||||
      }).length || 0
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [isRejectingId, setIsRejectingId] = useState<number | null>()
 | 
					  const [isRejectingId, setIsRejectingId] = useState<number | null>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -161,7 +155,7 @@ const HoldRequestNotifications = (props: Props) => {
 | 
				
			|||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <section className="py-6">
 | 
					    <section className="py-6">
 | 
				
			||||||
      <div className="mb-4 flex justify-between items-end">
 | 
					      <div className="mb-4 flex justify-between items-end">
 | 
				
			||||||
        <h3 className="px-4 text-lg font-semibold">Incoming Hold Requests</h3>
 | 
					        <h3 className="px-4 text-lg font-semibold">Inbound Hold Requests</h3>
 | 
				
			||||||
        {!!totalHoldNotifications && (
 | 
					        {!!totalHoldNotifications && (
 | 
				
			||||||
          <span className="font-bold">{totalHoldNotifications} Unaddressed</span>
 | 
					          <span className="font-bold">{totalHoldNotifications} Unaddressed</span>
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,17 +4,15 @@ import { Checkout, Repository, User } from '@/payload-types'
 | 
				
			|||||||
import { PaginatedDocs } from 'payload'
 | 
					import { PaginatedDocs } from 'payload'
 | 
				
			||||||
import RepoList from './RepoList'
 | 
					import RepoList from './RepoList'
 | 
				
			||||||
import HoldRequestNotifications from './HoldRequests'
 | 
					import HoldRequestNotifications from './HoldRequests'
 | 
				
			||||||
import BorrowedBooks from './BorrowedBooks'
 | 
					import CheckedOutBooks from './CheckedOutBooks'
 | 
				
			||||||
import LoanedBooks from './LoanedBooks'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
  repos: PaginatedDocs<Repository> | null
 | 
					  repos: PaginatedDocs<Repository> | null
 | 
				
			||||||
  borrows: PaginatedDocs<Checkout> | null
 | 
					  checkouts: PaginatedDocs<Checkout> | null
 | 
				
			||||||
  loans: PaginatedDocs<Checkout> | null
 | 
					 | 
				
			||||||
  user: User | null
 | 
					  user: User | null
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
const Manage = (props: Props) => {
 | 
					const Manage = (props: Props) => {
 | 
				
			||||||
  const { repos, borrows, loans, user } = props
 | 
					  const { repos, checkouts, user } = props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <section>
 | 
					    <section>
 | 
				
			||||||
@ -25,11 +23,7 @@ const Manage = (props: Props) => {
 | 
				
			|||||||
      <div>{user && <HoldRequestNotifications repos={repos} user={user} />}</div>
 | 
					      <div>{user && <HoldRequestNotifications repos={repos} user={user} />}</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        <BorrowedBooks initialBorrows={borrows} />
 | 
					        <CheckedOutBooks initialCheckoutPage={checkouts} />
 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      <div>
 | 
					 | 
				
			||||||
        <LoanedBooks initialLoans={loans} />
 | 
					 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </section>
 | 
					    </section>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
				
			|||||||
@ -28,24 +28,21 @@ import {
 | 
				
			|||||||
import { StackedLayout } from '@/components/stacked-layout'
 | 
					import { StackedLayout } from '@/components/stacked-layout'
 | 
				
			||||||
import { Media } from '@/payload-types'
 | 
					import { Media } from '@/payload-types'
 | 
				
			||||||
import { useGlobal } from '@/providers/GlobalProvider'
 | 
					import { useGlobal } from '@/providers/GlobalProvider'
 | 
				
			||||||
import { ArrowRightStartOnRectangleIcon, UserIcon } from '@heroicons/react/16/solid'
 | 
					import { ArrowRightStartOnRectangleIcon, LightBulbIcon, UserIcon } from '@heroicons/react/16/solid'
 | 
				
			||||||
import { MagnifyingGlassIcon, SunIcon, MoonIcon } from '@heroicons/react/20/solid'
 | 
					import { MagnifyingGlassIcon, SunIcon, MoonIcon } from '@heroicons/react/20/solid'
 | 
				
			||||||
import { useTheme } from 'next-themes'
 | 
					import { useTheme } from 'next-themes'
 | 
				
			||||||
import Link from 'next/link'
 | 
					import Link from 'next/link'
 | 
				
			||||||
import React, { ReactNode, useMemo } from 'react'
 | 
					import React, { useMemo } from 'react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type NavItem = {
 | 
					const navItems = [
 | 
				
			||||||
  label: string
 | 
					  { label: 'Home', url: '/' },
 | 
				
			||||||
  href: string
 | 
					  { label: 'Browse', url: '/books' },
 | 
				
			||||||
  newTab: boolean
 | 
					  { label: 'Events', url: '/events' },
 | 
				
			||||||
}
 | 
					  { label: 'Settings', url: '/settings' },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					export default function SiteNavigation(props: { children: React.ReactNode }) {
 | 
				
			||||||
  children: ReactNode
 | 
					  const { children } = props
 | 
				
			||||||
  navItems: NavItem[]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export default function SiteNavigation(props: Props) {
 | 
					 | 
				
			||||||
  const { children, navItems } = props
 | 
					 | 
				
			||||||
  const { theme, setTheme } = useTheme()
 | 
					  const { theme, setTheme } = useTheme()
 | 
				
			||||||
  const { user, setUser } = useGlobal()
 | 
					  const { user, setUser } = useGlobal()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -73,17 +70,19 @@ export default function SiteNavigation(props: Props) {
 | 
				
			|||||||
      navbar={
 | 
					      navbar={
 | 
				
			||||||
        <Navbar>
 | 
					        <Navbar>
 | 
				
			||||||
          <Dropdown>
 | 
					          <Dropdown>
 | 
				
			||||||
            <DropdownButton as={NavbarItem} className="max-lg:hidden">
 | 
					            <DropdownButton
 | 
				
			||||||
              <Link href="/" className="flex items-center justify-around gap-3">
 | 
					              as={NavbarItem}
 | 
				
			||||||
                <Avatar src="/api/media/file/bethel-logo.jpg" className="size-8" />
 | 
					              onClick={() => (window.location.href = '/')}
 | 
				
			||||||
 | 
					              className="max-lg:hidden"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              <Avatar src="/api/media/file/bethel-logo.jpg" />
 | 
				
			||||||
              <NavbarLabel>Midrashim</NavbarLabel>
 | 
					              <NavbarLabel>Midrashim</NavbarLabel>
 | 
				
			||||||
              </Link>
 | 
					 | 
				
			||||||
            </DropdownButton>
 | 
					            </DropdownButton>
 | 
				
			||||||
          </Dropdown>
 | 
					          </Dropdown>
 | 
				
			||||||
          <NavbarDivider className="max-lg:hidden" />
 | 
					          <NavbarDivider className="max-lg:hidden" />
 | 
				
			||||||
          <NavbarSection className="max-lg:hidden">
 | 
					          <NavbarSection className="max-lg:hidden">
 | 
				
			||||||
            {navItems.map(({ label, href, newTab }) => (
 | 
					            {navItems.map(({ label, url }) => (
 | 
				
			||||||
              <NavbarItem key={label} target={newTab ? '_blank' : ''} href={href}>
 | 
					              <NavbarItem key={label} href={url}>
 | 
				
			||||||
                {label}
 | 
					                {label}
 | 
				
			||||||
              </NavbarItem>
 | 
					              </NavbarItem>
 | 
				
			||||||
            ))}
 | 
					            ))}
 | 
				
			||||||
@ -117,6 +116,10 @@ export default function SiteNavigation(props: Props) {
 | 
				
			|||||||
                    <DropdownLabel>My profile</DropdownLabel>
 | 
					                    <DropdownLabel>My profile</DropdownLabel>
 | 
				
			||||||
                  </DropdownItem>
 | 
					                  </DropdownItem>
 | 
				
			||||||
                )}
 | 
					                )}
 | 
				
			||||||
 | 
					                <DropdownItem href="/feedback">
 | 
				
			||||||
 | 
					                  <LightBulbIcon />
 | 
				
			||||||
 | 
					                  <DropdownLabel>Share feedback</DropdownLabel>
 | 
				
			||||||
 | 
					                </DropdownItem>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                {!!user && (
 | 
					                {!!user && (
 | 
				
			||||||
                  <>
 | 
					                  <>
 | 
				
			||||||
@ -146,8 +149,8 @@ export default function SiteNavigation(props: Props) {
 | 
				
			|||||||
          </SidebarHeader>
 | 
					          </SidebarHeader>
 | 
				
			||||||
          <SidebarBody>
 | 
					          <SidebarBody>
 | 
				
			||||||
            <SidebarSection>
 | 
					            <SidebarSection>
 | 
				
			||||||
              {navItems.map(({ label, href, newTab }) => (
 | 
					              {navItems.map(({ label, url }) => (
 | 
				
			||||||
                <SidebarItem key={label} href={href} target={newTab ? '_blank' : ''}>
 | 
					                <SidebarItem key={label} href={url}>
 | 
				
			||||||
                  {label}
 | 
					                  {label}
 | 
				
			||||||
                </SidebarItem>
 | 
					                </SidebarItem>
 | 
				
			||||||
              ))}
 | 
					              ))}
 | 
				
			||||||
 | 
				
			|||||||
@ -8,8 +8,6 @@ import { Label } from '@/components/ui/label'
 | 
				
			|||||||
import { useRouter, useSearchParams } from 'next/navigation'
 | 
					import { useRouter, useSearchParams } from 'next/navigation'
 | 
				
			||||||
import { useState } from 'react'
 | 
					import { useState } from 'react'
 | 
				
			||||||
import { useGlobal } from '@/providers/GlobalProvider'
 | 
					import { useGlobal } from '@/providers/GlobalProvider'
 | 
				
			||||||
import { Loader2 } from 'lucide-react'
 | 
					 | 
				
			||||||
import { toast } from 'sonner'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function LoginForm({ className, ...props }: React.ComponentProps<'div'>) {
 | 
					export function LoginForm({ className, ...props }: React.ComponentProps<'div'>) {
 | 
				
			||||||
  const router = useRouter()
 | 
					  const router = useRouter()
 | 
				
			||||||
@ -41,19 +39,14 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
 | 
				
			|||||||
            password,
 | 
					            password,
 | 
				
			||||||
          }),
 | 
					          }),
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        const loginResponse = await loginReq.json()
 | 
					        const user = await loginReq.json()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (loginResponse.errors?.length) {
 | 
					        setUser(user.user)
 | 
				
			||||||
          loginResponse.errors.forEach((e: Error) => {
 | 
					 | 
				
			||||||
            toast(e.message)
 | 
					 | 
				
			||||||
          })
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (loginResponse.user) setUser(loginResponse.user)
 | 
					        if (user.token) router.push(searchParams.get('redirectUrl') || '/')
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
        if (loginResponse.token) router.push(searchParams.get('redirectUrl') || '/')
 | 
					        console.error('Login failed:', error)
 | 
				
			||||||
      } catch (_) {
 | 
					      } finally {
 | 
				
			||||||
        toast('Unknown issue while authenticating. Try again')
 | 
					 | 
				
			||||||
        setIsLoading(false)
 | 
					        setIsLoading(false)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -92,11 +85,16 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
 | 
				
			|||||||
                  </div>
 | 
					                  </div>
 | 
				
			||||||
                  <Input id="password" name="password" type="password" required />
 | 
					                  <Input id="password" name="password" type="password" required />
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <Button disabled={isLoading} type="submit" className="w-full">
 | 
					                <Button type="submit" className="w-full">
 | 
				
			||||||
                  {isLoading && <Loader2 />}
 | 
					                  Login
 | 
				
			||||||
                  <span>Login</span>
 | 
					 | 
				
			||||||
                </Button>
 | 
					                </Button>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
 | 
					              <div className="text-center text-sm">
 | 
				
			||||||
 | 
					                Don't have an account?{' '}
 | 
				
			||||||
 | 
					                <a href="/requestAccess" className="underline underline-offset-4">
 | 
				
			||||||
 | 
					                  Request Access
 | 
				
			||||||
 | 
					                </a>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </form>
 | 
					          </form>
 | 
				
			||||||
        </CardContent>
 | 
					        </CardContent>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +0,0 @@
 | 
				
			|||||||
import { loadEnvConfig } from '@next/env'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const projectDir = process.cwd()
 | 
					 | 
				
			||||||
loadEnvConfig(projectDir)
 | 
					 | 
				
			||||||
@ -21,8 +21,9 @@ export const Header: GlobalConfig = {
 | 
				
			|||||||
          type: 'text',
 | 
					          type: 'text',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          name: 'href',
 | 
					          name: 'page',
 | 
				
			||||||
          type: 'text'
 | 
					          type: 'relationship',
 | 
				
			||||||
 | 
					          relationTo: 'pages'
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          name: 'newTab',
 | 
					          name: 'newTab',
 | 
				
			||||||
 | 
				
			|||||||
@ -350,7 +350,7 @@ export interface Checkout {
 | 
				
			|||||||
  isReturned?: boolean | null;
 | 
					  isReturned?: boolean | null;
 | 
				
			||||||
  dateDue?: string | null;
 | 
					  dateDue?: string | null;
 | 
				
			||||||
  ownerVerifiedReturnedDate?: string | null;
 | 
					  ownerVerifiedReturnedDate?: string | null;
 | 
				
			||||||
  loaneeReturnedDate?: string | null;
 | 
					  loanerReturnedDate?: string | null;
 | 
				
			||||||
  book?: {
 | 
					  book?: {
 | 
				
			||||||
    docs?: (number | Copy)[];
 | 
					    docs?: (number | Copy)[];
 | 
				
			||||||
    hasNextPage?: boolean;
 | 
					    hasNextPage?: boolean;
 | 
				
			||||||
@ -598,7 +598,7 @@ export interface CheckoutsSelect<T extends boolean = true> {
 | 
				
			|||||||
  isReturned?: T;
 | 
					  isReturned?: T;
 | 
				
			||||||
  dateDue?: T;
 | 
					  dateDue?: T;
 | 
				
			||||||
  ownerVerifiedReturnedDate?: T;
 | 
					  ownerVerifiedReturnedDate?: T;
 | 
				
			||||||
  loaneeReturnedDate?: T;
 | 
					  loanerReturnedDate?: T;
 | 
				
			||||||
  book?: T;
 | 
					  book?: T;
 | 
				
			||||||
  updatedAt?: T;
 | 
					  updatedAt?: T;
 | 
				
			||||||
  createdAt?: T;
 | 
					  createdAt?: T;
 | 
				
			||||||
@ -678,7 +678,7 @@ export interface Header {
 | 
				
			|||||||
  headerLinks?:
 | 
					  headerLinks?:
 | 
				
			||||||
    | {
 | 
					    | {
 | 
				
			||||||
        label?: string | null;
 | 
					        label?: string | null;
 | 
				
			||||||
        href?: string | null;
 | 
					        page?: (number | null) | Page;
 | 
				
			||||||
        newTab?: boolean | null;
 | 
					        newTab?: boolean | null;
 | 
				
			||||||
        id?: string | null;
 | 
					        id?: string | null;
 | 
				
			||||||
      }[]
 | 
					      }[]
 | 
				
			||||||
@ -696,7 +696,7 @@ export interface HeaderSelect<T extends boolean = true> {
 | 
				
			|||||||
    | T
 | 
					    | T
 | 
				
			||||||
    | {
 | 
					    | {
 | 
				
			||||||
        label?: T;
 | 
					        label?: T;
 | 
				
			||||||
        href?: T;
 | 
					        page?: T;
 | 
				
			||||||
        newTab?: T;
 | 
					        newTab?: T;
 | 
				
			||||||
        id?: T;
 | 
					        id?: T;
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
				
			|||||||
@ -19,8 +19,6 @@ import { Pages } from './collections/Pages/Pages'
 | 
				
			|||||||
import HoldRequests from './collections/Checkouts/HoldRequests'
 | 
					import HoldRequests from './collections/Checkouts/HoldRequests'
 | 
				
			||||||
import Checkouts from './collections/Checkouts/Checkouts'
 | 
					import Checkouts from './collections/Checkouts/Checkouts'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import './envConfig.ts'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const filename = fileURLToPath(import.meta.url)
 | 
					const filename = fileURLToPath(import.meta.url)
 | 
				
			||||||
const dirname = path.dirname(filename)
 | 
					const dirname = path.dirname(filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -32,8 +30,8 @@ export default buildConfig({
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    dateFormat: 'MM/dd/yyyy',
 | 
					    dateFormat: 'MM/dd/yyyy',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  cors: [process.env.SERVER_URL || ''],
 | 
					  //cors: [process.env.DOMAIN_NAME || ''],
 | 
				
			||||||
  csrf: [process.env.SERVER_URL || ''],
 | 
					  //csrf: [process.env.DOMAIN_NAME || ''],
 | 
				
			||||||
  upload: {
 | 
					  upload: {
 | 
				
			||||||
    limits: {
 | 
					    limits: {
 | 
				
			||||||
      fileSize: 5000000, // in bytes
 | 
					      fileSize: 5000000, // in bytes
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ type GlobalProps = {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type GlobalState = {
 | 
					type GlobalState = {
 | 
				
			||||||
  setUser: (user?: User) => void
 | 
					  setUser: (users?: User) => void
 | 
				
			||||||
} & GlobalProps
 | 
					} & GlobalProps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const defaultState = {
 | 
					const defaultState = {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,53 +0,0 @@
 | 
				
			|||||||
'use server'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { getPayload } from 'payload'
 | 
					 | 
				
			||||||
import config from '@/payload.config'
 | 
					 | 
				
			||||||
import type { PaginatedDocs } from "payload"
 | 
					 | 
				
			||||||
import { Checkout } from '@/payload-types'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Props = {
 | 
					 | 
				
			||||||
  userId: number
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export const getUserBorrows = async (props: Props) => {
 | 
					 | 
				
			||||||
  const { userId } = props
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const payloadConfig = await config
 | 
					 | 
				
			||||||
  const payload = await getPayload({ config: payloadConfig })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let userBorrows: PaginatedDocs<Checkout> | null = null
 | 
					 | 
				
			||||||
  if (userId)
 | 
					 | 
				
			||||||
    userBorrows = await payload.find({
 | 
					 | 
				
			||||||
      collection: 'checkouts',
 | 
					 | 
				
			||||||
      depth: 3,
 | 
					 | 
				
			||||||
      limit: 10,
 | 
					 | 
				
			||||||
      select: {
 | 
					 | 
				
			||||||
        id: true,
 | 
					 | 
				
			||||||
        copy: true,
 | 
					 | 
				
			||||||
        dateDue: true,
 | 
					 | 
				
			||||||
        ownerVerifiedReturnedDate: true,
 | 
					 | 
				
			||||||
        loaneeReturnedDate: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      sort: 'dateDue',
 | 
					 | 
				
			||||||
      where: {
 | 
					 | 
				
			||||||
        and: [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            isReturned: {
 | 
					 | 
				
			||||||
              not_equals: true,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            'user.id': {
 | 
					 | 
				
			||||||
              equals: userId,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            ownerVerifiedReturnedDate: {
 | 
					 | 
				
			||||||
              equals: null,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    }) as PaginatedDocs<Checkout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return userBorrows
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
'use server'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { getPayload } from 'payload'
 | 
					 | 
				
			||||||
import config from '@/payload.config'
 | 
					 | 
				
			||||||
import type { PaginatedDocs } from "payload"
 | 
					 | 
				
			||||||
import { Checkout } from '@/payload-types'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Props = {
 | 
					 | 
				
			||||||
  userId: number
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export const getUserLoans = async (props: Props) => {
 | 
					 | 
				
			||||||
  const { userId } = props
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const payloadConfig = await config
 | 
					 | 
				
			||||||
  const payload = await getPayload({ config: payloadConfig })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let userLoans: PaginatedDocs<Checkout> | null = null
 | 
					 | 
				
			||||||
  if (userId)
 | 
					 | 
				
			||||||
    userLoans = await payload.find({
 | 
					 | 
				
			||||||
      collection: 'checkouts',
 | 
					 | 
				
			||||||
      depth: 3,
 | 
					 | 
				
			||||||
      limit: 10,
 | 
					 | 
				
			||||||
      select: {
 | 
					 | 
				
			||||||
        id: true,
 | 
					 | 
				
			||||||
        copy: true,
 | 
					 | 
				
			||||||
        dateDue: true,
 | 
					 | 
				
			||||||
        ownerVerifiedReturnedDate: true,
 | 
					 | 
				
			||||||
        loaneeReturnedDate: true,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      sort: 'dateDue',
 | 
					 | 
				
			||||||
      where: {
 | 
					 | 
				
			||||||
        and: [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            isReturned: {
 | 
					 | 
				
			||||||
              not_equals: true,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            'user.id': {
 | 
					 | 
				
			||||||
              equals: userId,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    }) as PaginatedDocs<Checkout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return userLoans
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -17,14 +17,16 @@ export const resetPassword = async (props: Props): Promise<boolean> => {
 | 
				
			|||||||
  const payload = await getPayload({ config: payloadConfig })
 | 
					  const payload = await getPayload({ config: payloadConfig })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    await payload.resetPassword({
 | 
					    const result = await payload.resetPassword({
 | 
				
			||||||
      collection: 'users',
 | 
					      collection: 'users',
 | 
				
			||||||
      overrideAccess: true,
 | 
					      overrideAccess: false,
 | 
				
			||||||
      data: {
 | 
					      data: {
 | 
				
			||||||
        password: password,
 | 
					        password: password,
 | 
				
			||||||
        token: token,
 | 
					        token: token,
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					    console.log('result')
 | 
				
			||||||
 | 
					    console.log(result)
 | 
				
			||||||
    return true
 | 
					    return true
 | 
				
			||||||
  } catch (err) {
 | 
					  } catch (err) {
 | 
				
			||||||
    console.log(err)
 | 
					    console.log(err)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,51 +0,0 @@
 | 
				
			|||||||
'use server'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { getPayload } from 'payload'
 | 
					 | 
				
			||||||
import config from '@/payload.config'
 | 
					 | 
				
			||||||
import { Checkout } from '@/payload-types'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Props = {
 | 
					 | 
				
			||||||
  checkoutId: number
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export const loaneeReturnCheckout = async (props: Props): Promise<Checkout | null> => {
 | 
					 | 
				
			||||||
  const { checkoutId } = props
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const payloadConfig = await config
 | 
					 | 
				
			||||||
  const payload = await getPayload({ config: payloadConfig })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  try {
 | 
					 | 
				
			||||||
    const updatedCheckout = await payload.update({
 | 
					 | 
				
			||||||
      collection: 'checkouts',
 | 
					 | 
				
			||||||
      id: checkoutId,
 | 
					 | 
				
			||||||
      data: {
 | 
					 | 
				
			||||||
        loaneeReturnedDate: new Date().toDateString(),
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    return updatedCheckout
 | 
					 | 
				
			||||||
  } catch (err) {
 | 
					 | 
				
			||||||
    console.log(err)
 | 
					 | 
				
			||||||
    return null
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const ownerReturnCheckout = async (props: Props): Promise<Checkout | null> => {
 | 
					 | 
				
			||||||
  const { checkoutId } = props
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const payloadConfig = await config
 | 
					 | 
				
			||||||
  const payload = await getPayload({ config: payloadConfig })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  try {
 | 
					 | 
				
			||||||
    const updatedCheckout = await payload.update({
 | 
					 | 
				
			||||||
      collection: 'checkouts',
 | 
					 | 
				
			||||||
      id: checkoutId,
 | 
					 | 
				
			||||||
      data: {
 | 
					 | 
				
			||||||
        ownerVerifiedReturnedDate: new Date().toDateString(),
 | 
					 | 
				
			||||||
        isReturned: true,
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    return updatedCheckout
 | 
					 | 
				
			||||||
  } catch (err) {
 | 
					 | 
				
			||||||
    console.log(err)
 | 
					 | 
				
			||||||
    return null
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user