AdminList.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { FunctionComponent } from 'react'
  2. import AdminPage from './AdminPage'
  3. import Link from 'next/link'
  4. import theme from '../../styles/theme'
  5. import { QueryResult, MutationTuple, QueryHookOptions, MutationHookOptions } from '@apollo/client'
  6. import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
  7. import { faEdit, faTrash, faPlusCircle } from '@fortawesome/free-solid-svg-icons'
  8. interface IAdminList<TQueryData, TQueryVariables, TDeleteData, TDeleteVariables> {
  9. name: string
  10. adminMenu?: string
  11. dataKey: keyof TQueryData
  12. get: (
  13. baseOptions?: QueryHookOptions<TQueryData, TQueryVariables>
  14. ) => QueryResult<TQueryData, TQueryVariables>
  15. remove?: (
  16. baseOptions?: MutationHookOptions<TDeleteData, TDeleteVariables>
  17. ) => MutationTuple<TDeleteData, TDeleteVariables>
  18. Component: FunctionComponent<any>
  19. }
  20. const AdminList = <
  21. TQueryData extends { [dataKey: string]: any[] },
  22. TQueryVariables,
  23. TDeleteData,
  24. TDeleteVariables
  25. >({
  26. name,
  27. adminMenu,
  28. dataKey,
  29. get,
  30. remove,
  31. Component,
  32. }: IAdminList<TQueryData, TQueryVariables, TDeleteData, TDeleteVariables>) => {
  33. const [removeFunction, removeResult] = remove ? remove() : [undefined, undefined]
  34. const { data, error, loading, refetch } = get({ fetchPolicy: 'network-only' })
  35. let content
  36. if (loading) content = <p>Loading data...</p>
  37. else if (error) content = <p>Error loading data.</p>
  38. else if (!data) content = <p>No data found.</p>
  39. else
  40. content = (
  41. <ul>
  42. {data[dataKey].map((item: any) => (
  43. <li key={item.id}>
  44. <Component key={item.id} item={item} className='admin-component' />
  45. <div className='admin-toolbar'>
  46. <Link href={`/${adminMenu}/[id]`} as={`${adminMenu}/${item.id}`}>
  47. <a title='edit'>
  48. <button type='button'>
  49. <FontAwesomeIcon icon={faEdit} height={16} />
  50. </button>
  51. </a>
  52. </Link>
  53. {removeFunction && (
  54. <button
  55. onClick={async (event) => {
  56. const deletedItem = await removeFunction({ variables: { id: item.id } } as any)
  57. if (deletedItem) refetch()
  58. }}
  59. disabled={removeResult?.loading}
  60. title='delete'
  61. >
  62. <FontAwesomeIcon icon={faTrash} height={16} />
  63. </button>
  64. )}
  65. </div>
  66. </li>
  67. ))}
  68. <style jsx>{`
  69. ul {
  70. padding: 0;
  71. }
  72. ul :global(li) {
  73. padding: 0.2em 0.5em;
  74. display: flex;
  75. list-style: none;
  76. align-items: center;
  77. background-color: #0770;
  78. transition: background-color 250ms ease-in-out;
  79. border-top: 1px solid #0002;
  80. position: relative;
  81. }
  82. ul :global(li:last-child) {
  83. border-bottom: 1px solid #0002;
  84. }
  85. ul :global(li:hover) {
  86. background-color: ${theme.colors.formHighlightBackground}22;
  87. }
  88. ul :global(.admin-component) {
  89. flex-grow: 1;
  90. }
  91. ul :global(.admin-toolbar button) {
  92. padding: 0.4em;
  93. margin: 0;
  94. color: ${theme.colors.buttonBackground};
  95. background-color: transparent;
  96. }
  97. ul :global(a) {
  98. color: ${theme.colors.highlight};
  99. text-decoration: none;
  100. }
  101. ul :global(button > a) {
  102. color: ${theme.colors.button};
  103. text-decoration: none;
  104. }
  105. ul :global(li .admin-toolbar) {
  106. display: none;
  107. position: absolute;
  108. top: 0;
  109. right: 0;
  110. width: 80px;
  111. background-color: ${theme.colors.background};
  112. box-shadow: ${theme.bsSmall};
  113. }
  114. ul :global(li:hover .admin-toolbar) {
  115. display: flex;
  116. }
  117. `}</style>
  118. </ul>
  119. )
  120. return (
  121. <AdminPage>
  122. <h2>{name}</h2>
  123. <Link href={`${adminMenu}/create`}>
  124. <a>
  125. <button>
  126. <FontAwesomeIcon icon={faPlusCircle} height={36} />
  127. </button>
  128. </a>
  129. </Link>
  130. {content}
  131. <style jsx>{`
  132. button {
  133. color: ${theme.colors.buttonBackground};
  134. background-color: transparent;
  135. display: inline;
  136. margin: 0.4rem;
  137. padding: 0;
  138. }
  139. `}</style>
  140. </AdminPage>
  141. )
  142. }
  143. export default AdminList