Add more pages using v0
This commit is contained in:
		@@ -52,6 +52,7 @@
 | 
				
			|||||||
    "react-day-picker": "8.10.1",
 | 
					    "react-day-picker": "8.10.1",
 | 
				
			||||||
    "react-dom": "^18.3.1",
 | 
					    "react-dom": "^18.3.1",
 | 
				
			||||||
    "react-hook-form": "^7.52.2",
 | 
					    "react-hook-form": "^7.52.2",
 | 
				
			||||||
 | 
					    "recharts": "^2.13.3",
 | 
				
			||||||
    "server-only": "^0.0.1",
 | 
					    "server-only": "^0.0.1",
 | 
				
			||||||
    "sonner": "^1.5.0",
 | 
					    "sonner": "^1.5.0",
 | 
				
			||||||
    "tailwind-merge": "^2.4.0",
 | 
					    "tailwind-merge": "^2.4.0",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										257
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										257
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -113,6 +113,9 @@ importers:
 | 
				
			|||||||
      react-hook-form:
 | 
					      react-hook-form:
 | 
				
			||||||
        specifier: ^7.52.2
 | 
					        specifier: ^7.52.2
 | 
				
			||||||
        version: 7.52.2(react@18.3.1)
 | 
					        version: 7.52.2(react@18.3.1)
 | 
				
			||||||
 | 
					      recharts:
 | 
				
			||||||
 | 
					        specifier: ^2.13.3
 | 
				
			||||||
 | 
					        version: 2.13.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
 | 
				
			||||||
      server-only:
 | 
					      server-only:
 | 
				
			||||||
        specifier: ^0.0.1
 | 
					        specifier: ^0.0.1
 | 
				
			||||||
        version: 0.0.1
 | 
					        version: 0.0.1
 | 
				
			||||||
@@ -1489,6 +1492,33 @@ packages:
 | 
				
			|||||||
  '@types/cookie@0.6.0':
 | 
					  '@types/cookie@0.6.0':
 | 
				
			||||||
    resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
 | 
					    resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-array@3.2.1':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-color@3.1.3':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-ease@3.0.2':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-interpolate@3.0.4':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-path@3.1.0':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-scale@4.0.8':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-shape@3.1.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-time@3.0.4':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-timer@3.0.2':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@types/eslint@8.56.11':
 | 
					  '@types/eslint@8.56.11':
 | 
				
			||||||
    resolution: {integrity: sha512-sVBpJMf7UPo/wGecYOpk2aQya2VUGeHhe38WG7/mN5FufNSubf5VT9Uh9Uyp8/eLJpu1/tuhJ/qTo4mhSB4V4Q==}
 | 
					    resolution: {integrity: sha512-sVBpJMf7UPo/wGecYOpk2aQya2VUGeHhe38WG7/mN5FufNSubf5VT9Uh9Uyp8/eLJpu1/tuhJ/qTo4mhSB4V4Q==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1829,6 +1859,50 @@ packages:
 | 
				
			|||||||
  csstype@3.1.3:
 | 
					  csstype@3.1.3:
 | 
				
			||||||
    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 | 
					    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-array@3.2.4:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-color@3.1.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-ease@3.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-format@3.1.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-interpolate@3.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-path@3.1.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-scale@4.0.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-shape@3.2.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-time-format@4.1.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-time@3.1.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-timer@3.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  d@1.0.2:
 | 
					  d@1.0.2:
 | 
				
			||||||
    resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
 | 
					    resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
 | 
				
			||||||
    engines: {node: '>=0.12'}
 | 
					    engines: {node: '>=0.12'}
 | 
				
			||||||
@@ -1868,6 +1942,9 @@ packages:
 | 
				
			|||||||
      supports-color:
 | 
					      supports-color:
 | 
				
			||||||
        optional: true
 | 
					        optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  decimal.js-light@2.5.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  deep-equal@2.2.3:
 | 
					  deep-equal@2.2.3:
 | 
				
			||||||
    resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==}
 | 
					    resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==}
 | 
				
			||||||
    engines: {node: '>= 0.4'}
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
@@ -1911,6 +1988,9 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
 | 
					    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
 | 
				
			||||||
    engines: {node: '>=6.0.0'}
 | 
					    engines: {node: '>=6.0.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  dom-helpers@5.2.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  dotenv@16.4.5:
 | 
					  dotenv@16.4.5:
 | 
				
			||||||
    resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
 | 
					    resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
 | 
				
			||||||
    engines: {node: '>=12'}
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
@@ -2208,12 +2288,19 @@ packages:
 | 
				
			|||||||
  event-emitter@0.3.5:
 | 
					  event-emitter@0.3.5:
 | 
				
			||||||
    resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
 | 
					    resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  eventemitter3@4.0.7:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ext@1.7.0:
 | 
					  ext@1.7.0:
 | 
				
			||||||
    resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
 | 
					    resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fast-deep-equal@3.1.3:
 | 
					  fast-deep-equal@3.1.3:
 | 
				
			||||||
    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
 | 
					    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fast-equals@5.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>=6.0.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fast-glob@3.3.2:
 | 
					  fast-glob@3.3.2:
 | 
				
			||||||
    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
 | 
					    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
 | 
				
			||||||
    engines: {node: '>=8.6.0'}
 | 
					    engines: {node: '>=8.6.0'}
 | 
				
			||||||
@@ -2393,6 +2480,10 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
 | 
					    resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
 | 
				
			||||||
    engines: {node: '>= 0.4'}
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internmap@2.0.3:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  invariant@2.2.4:
 | 
					  invariant@2.2.4:
 | 
				
			||||||
    resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
 | 
					    resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2629,6 +2720,9 @@ packages:
 | 
				
			|||||||
  lodash.throttle@4.1.1:
 | 
					  lodash.throttle@4.1.1:
 | 
				
			||||||
    resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
 | 
					    resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lodash@4.17.21:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  loose-envify@1.4.0:
 | 
					  loose-envify@1.4.0:
 | 
				
			||||||
    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
 | 
					    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
 | 
				
			||||||
    hasBin: true
 | 
					    hasBin: true
 | 
				
			||||||
@@ -3053,6 +3147,9 @@ packages:
 | 
				
			|||||||
  react-is@16.13.1:
 | 
					  react-is@16.13.1:
 | 
				
			||||||
    resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
 | 
					    resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  react-is@18.3.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  react-remove-scroll-bar@2.3.6:
 | 
					  react-remove-scroll-bar@2.3.6:
 | 
				
			||||||
    resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
 | 
					    resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
 | 
				
			||||||
    engines: {node: '>=10'}
 | 
					    engines: {node: '>=10'}
 | 
				
			||||||
@@ -3093,6 +3190,12 @@ packages:
 | 
				
			|||||||
      '@types/react':
 | 
					      '@types/react':
 | 
				
			||||||
        optional: true
 | 
					        optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  react-smooth@4.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      react: ^16.8.0 || ^17.0.0 || ^18.0.0
 | 
				
			||||||
 | 
					      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  react-style-singleton@2.2.1:
 | 
					  react-style-singleton@2.2.1:
 | 
				
			||||||
    resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
 | 
					    resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
 | 
				
			||||||
    engines: {node: '>=10'}
 | 
					    engines: {node: '>=10'}
 | 
				
			||||||
@@ -3103,6 +3206,12 @@ packages:
 | 
				
			|||||||
      '@types/react':
 | 
					      '@types/react':
 | 
				
			||||||
        optional: true
 | 
					        optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  react-transition-group@4.4.5:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      react: '>=16.6.0'
 | 
				
			||||||
 | 
					      react-dom: '>=16.6.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  react@18.3.1:
 | 
					  react@18.3.1:
 | 
				
			||||||
    resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
 | 
					    resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
 | 
				
			||||||
    engines: {node: '>=0.10.0'}
 | 
					    engines: {node: '>=0.10.0'}
 | 
				
			||||||
@@ -3114,6 +3223,16 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
 | 
					    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
 | 
				
			||||||
    engines: {node: '>=8.10.0'}
 | 
					    engines: {node: '>=8.10.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  recharts-scale@0.4.5:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  recharts@2.13.3:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-YDZ9dOfK9t3ycwxgKbrnDlRC4BHdjlY73fet3a0C1+qGMjXVZe6+VXmpOIIhzkje5MMEL8AN4hLIe4AMskBzlA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=14'}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      react: ^16.0.0 || ^17.0.0 || ^18.0.0
 | 
				
			||||||
 | 
					      react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reflect.getprototypeof@1.0.6:
 | 
					  reflect.getprototypeof@1.0.6:
 | 
				
			||||||
    resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
 | 
					    resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
 | 
				
			||||||
    engines: {node: '>= 0.4'}
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
@@ -3340,6 +3459,9 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==}
 | 
					    resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==}
 | 
				
			||||||
    engines: {node: '>=0.12'}
 | 
					    engines: {node: '>=0.12'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tiny-invariant@1.3.3:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  to-regex-range@5.0.1:
 | 
					  to-regex-range@5.0.1:
 | 
				
			||||||
    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
 | 
					    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
 | 
				
			||||||
    engines: {node: '>=8.0'}
 | 
					    engines: {node: '>=8.0'}
 | 
				
			||||||
@@ -3451,6 +3573,9 @@ packages:
 | 
				
			|||||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
					      react: ^16.8 || ^17.0 || ^18.0
 | 
				
			||||||
      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
					      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  victory-vendor@36.9.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  which-boxed-primitive@1.0.2:
 | 
					  which-boxed-primitive@1.0.2:
 | 
				
			||||||
    resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
 | 
					    resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -4582,6 +4707,30 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  '@types/cookie@0.6.0': {}
 | 
					  '@types/cookie@0.6.0': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-array@3.2.1': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-color@3.1.3': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-ease@3.0.2': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-interpolate@3.0.4':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@types/d3-color': 3.1.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-path@3.1.0': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-scale@4.0.8':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@types/d3-time': 3.0.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-shape@3.1.6':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@types/d3-path': 3.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-time@3.0.4': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@types/d3-timer@3.0.2': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@types/eslint@8.56.11':
 | 
					  '@types/eslint@8.56.11':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@types/estree': 1.0.5
 | 
					      '@types/estree': 1.0.5
 | 
				
			||||||
@@ -4975,6 +5124,44 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  csstype@3.1.3: {}
 | 
					  csstype@3.1.3: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-array@3.2.4:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      internmap: 2.0.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-color@3.1.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-ease@3.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-format@3.1.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-interpolate@3.0.1:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      d3-color: 3.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-path@3.1.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-scale@4.0.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      d3-array: 3.2.4
 | 
				
			||||||
 | 
					      d3-format: 3.1.0
 | 
				
			||||||
 | 
					      d3-interpolate: 3.0.1
 | 
				
			||||||
 | 
					      d3-time: 3.1.0
 | 
				
			||||||
 | 
					      d3-time-format: 4.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-shape@3.2.0:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      d3-path: 3.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-time-format@4.1.0:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      d3-time: 3.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-time@3.1.0:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      d3-array: 3.2.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d3-timer@3.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  d@1.0.2:
 | 
					  d@1.0.2:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      es5-ext: 0.10.64
 | 
					      es5-ext: 0.10.64
 | 
				
			||||||
@@ -5010,6 +5197,8 @@ snapshots:
 | 
				
			|||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      ms: 2.1.2
 | 
					      ms: 2.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  decimal.js-light@2.5.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  deep-equal@2.2.3:
 | 
					  deep-equal@2.2.3:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      array-buffer-byte-length: 1.0.1
 | 
					      array-buffer-byte-length: 1.0.1
 | 
				
			||||||
@@ -5069,6 +5258,11 @@ snapshots:
 | 
				
			|||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      esutils: 2.0.3
 | 
					      esutils: 2.0.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  dom-helpers@5.2.1:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@babel/runtime': 7.25.6
 | 
				
			||||||
 | 
					      csstype: 3.1.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  dotenv@16.4.5: {}
 | 
					  dotenv@16.4.5: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  dreamopt@0.8.0:
 | 
					  dreamopt@0.8.0:
 | 
				
			||||||
@@ -5550,12 +5744,16 @@ snapshots:
 | 
				
			|||||||
      d: 1.0.2
 | 
					      d: 1.0.2
 | 
				
			||||||
      es5-ext: 0.10.64
 | 
					      es5-ext: 0.10.64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  eventemitter3@4.0.7: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ext@1.7.0:
 | 
					  ext@1.7.0:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      type: 2.7.3
 | 
					      type: 2.7.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fast-deep-equal@3.1.3: {}
 | 
					  fast-deep-equal@3.1.3: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fast-equals@5.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fast-glob@3.3.2:
 | 
					  fast-glob@3.3.2:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@nodelib/fs.stat': 2.0.5
 | 
					      '@nodelib/fs.stat': 2.0.5
 | 
				
			||||||
@@ -5759,6 +5957,8 @@ snapshots:
 | 
				
			|||||||
      hasown: 2.0.2
 | 
					      hasown: 2.0.2
 | 
				
			||||||
      side-channel: 1.0.6
 | 
					      side-channel: 1.0.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internmap@2.0.3: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  invariant@2.2.4:
 | 
					  invariant@2.2.4:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      loose-envify: 1.4.0
 | 
					      loose-envify: 1.4.0
 | 
				
			||||||
@@ -5992,6 +6192,8 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  lodash.throttle@4.1.1: {}
 | 
					  lodash.throttle@4.1.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lodash@4.17.21: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  loose-envify@1.4.0:
 | 
					  loose-envify@1.4.0:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      js-tokens: 4.0.0
 | 
					      js-tokens: 4.0.0
 | 
				
			||||||
@@ -6345,6 +6547,8 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  react-is@16.13.1: {}
 | 
					  react-is@16.13.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  react-is@18.3.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  react-remove-scroll-bar@2.3.6(@types/react@18.3.3)(react@18.3.1):
 | 
					  react-remove-scroll-bar@2.3.6(@types/react@18.3.3)(react@18.3.1):
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      react: 18.3.1
 | 
					      react: 18.3.1
 | 
				
			||||||
@@ -6386,6 +6590,14 @@ snapshots:
 | 
				
			|||||||
    optionalDependencies:
 | 
					    optionalDependencies:
 | 
				
			||||||
      '@types/react': 18.3.3
 | 
					      '@types/react': 18.3.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  react-smooth@4.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      fast-equals: 5.0.1
 | 
				
			||||||
 | 
					      prop-types: 15.8.1
 | 
				
			||||||
 | 
					      react: 18.3.1
 | 
				
			||||||
 | 
					      react-dom: 18.3.1(react@18.3.1)
 | 
				
			||||||
 | 
					      react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  react-style-singleton@2.2.1(@types/react@18.3.3)(react@18.3.1):
 | 
					  react-style-singleton@2.2.1(@types/react@18.3.3)(react@18.3.1):
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      get-nonce: 1.0.1
 | 
					      get-nonce: 1.0.1
 | 
				
			||||||
@@ -6395,6 +6607,15 @@ snapshots:
 | 
				
			|||||||
    optionalDependencies:
 | 
					    optionalDependencies:
 | 
				
			||||||
      '@types/react': 18.3.3
 | 
					      '@types/react': 18.3.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@babel/runtime': 7.25.6
 | 
				
			||||||
 | 
					      dom-helpers: 5.2.1
 | 
				
			||||||
 | 
					      loose-envify: 1.4.0
 | 
				
			||||||
 | 
					      prop-types: 15.8.1
 | 
				
			||||||
 | 
					      react: 18.3.1
 | 
				
			||||||
 | 
					      react-dom: 18.3.1(react@18.3.1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  react@18.3.1:
 | 
					  react@18.3.1:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      loose-envify: 1.4.0
 | 
					      loose-envify: 1.4.0
 | 
				
			||||||
@@ -6407,6 +6628,23 @@ snapshots:
 | 
				
			|||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      picomatch: 2.3.1
 | 
					      picomatch: 2.3.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  recharts-scale@0.4.5:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      decimal.js-light: 2.5.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  recharts@2.13.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      clsx: 2.1.1
 | 
				
			||||||
 | 
					      eventemitter3: 4.0.7
 | 
				
			||||||
 | 
					      lodash: 4.17.21
 | 
				
			||||||
 | 
					      react: 18.3.1
 | 
				
			||||||
 | 
					      react-dom: 18.3.1(react@18.3.1)
 | 
				
			||||||
 | 
					      react-is: 18.3.1
 | 
				
			||||||
 | 
					      react-smooth: 4.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
 | 
				
			||||||
 | 
					      recharts-scale: 0.4.5
 | 
				
			||||||
 | 
					      tiny-invariant: 1.3.3
 | 
				
			||||||
 | 
					      victory-vendor: 36.9.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  reflect.getprototypeof@1.0.6:
 | 
					  reflect.getprototypeof@1.0.6:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      call-bind: 1.0.7
 | 
					      call-bind: 1.0.7
 | 
				
			||||||
@@ -6674,6 +6912,8 @@ snapshots:
 | 
				
			|||||||
      es5-ext: 0.10.64
 | 
					      es5-ext: 0.10.64
 | 
				
			||||||
      next-tick: 1.1.0
 | 
					      next-tick: 1.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tiny-invariant@1.3.3: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  to-regex-range@5.0.1:
 | 
					  to-regex-range@5.0.1:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      is-number: 7.0.0
 | 
					      is-number: 7.0.0
 | 
				
			||||||
@@ -6801,6 +7041,23 @@ snapshots:
 | 
				
			|||||||
      - '@types/react'
 | 
					      - '@types/react'
 | 
				
			||||||
      - '@types/react-dom'
 | 
					      - '@types/react-dom'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  victory-vendor@36.9.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@types/d3-array': 3.2.1
 | 
				
			||||||
 | 
					      '@types/d3-ease': 3.0.2
 | 
				
			||||||
 | 
					      '@types/d3-interpolate': 3.0.4
 | 
				
			||||||
 | 
					      '@types/d3-scale': 4.0.8
 | 
				
			||||||
 | 
					      '@types/d3-shape': 3.1.6
 | 
				
			||||||
 | 
					      '@types/d3-time': 3.0.4
 | 
				
			||||||
 | 
					      '@types/d3-timer': 3.0.2
 | 
				
			||||||
 | 
					      d3-array: 3.2.4
 | 
				
			||||||
 | 
					      d3-ease: 3.0.1
 | 
				
			||||||
 | 
					      d3-interpolate: 3.0.1
 | 
				
			||||||
 | 
					      d3-scale: 4.0.2
 | 
				
			||||||
 | 
					      d3-shape: 3.2.0
 | 
				
			||||||
 | 
					      d3-time: 3.1.0
 | 
				
			||||||
 | 
					      d3-timer: 3.0.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  which-boxed-primitive@1.0.2:
 | 
					  which-boxed-primitive@1.0.2:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      is-bigint: 1.0.4
 | 
					      is-bigint: 1.0.4
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										161
									
								
								src/app/account/billtracker/page.tsx
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										161
									
								
								src/app/account/billtracker/page.tsx
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							@@ -1,19 +1,148 @@
 | 
				
			|||||||
"use server"
 | 
					'use client'
 | 
				
			||||||
import { auth } from "~/auth"
 | 
					 | 
				
			||||||
import BreadCrumbBillTracker from "~/components/portal/home/breadcrumb/BreadCrumbBillTracker"
 | 
					 | 
				
			||||||
import BillTrackerCalendar from "~/components/portal/billtracker/BillTrackerCalendar"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function HomePage() {
 | 
					import { useState } from 'react'
 | 
				
			||||||
  const session = await auth()
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
  if (!session?.user) return <></>
 | 
					import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
    return (
 | 
					import { Calendar } from "~/components/ui/calendar"
 | 
				
			||||||
      <div className="w-2/3 flex flex-col p-6">
 | 
					import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"
 | 
				
			||||||
        <div className="flex flex-row">
 | 
					import { Input } from "~/components/ui/input"
 | 
				
			||||||
          <div className="">
 | 
					import { Label } from "~/components/ui/label"
 | 
				
			||||||
            <BreadCrumbBillTracker />
 | 
					import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"
 | 
				
			||||||
          </div>
 | 
					import { Plus, DollarSign } from 'lucide-react'
 | 
				
			||||||
        </div>
 | 
					
 | 
				
			||||||
        < BillTrackerCalendar />
 | 
					// Mock data for bills
 | 
				
			||||||
 | 
					const initialBills = [
 | 
				
			||||||
 | 
					  { id: 1, name: 'Electricity', amount: 80, dueDate: new Date(2023, 6, 15), category: 'Utilities' },
 | 
				
			||||||
 | 
					  { id: 2, name: 'Internet', amount: 60, dueDate: new Date(2023, 6, 20), category: 'Utilities' },
 | 
				
			||||||
 | 
					  { id: 3, name: 'Water', amount: 40, dueDate: new Date(2023, 6, 25), category: 'Utilities' },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function BillTrackerPage() {
 | 
				
			||||||
 | 
					  const [bills, setBills] = useState(initialBills)
 | 
				
			||||||
 | 
					  const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date())
 | 
				
			||||||
 | 
					  const [isDialogOpen, setIsDialogOpen] = useState(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleAddBill = (event: React.FormEvent<HTMLFormElement>) => {
 | 
				
			||||||
 | 
					    event.preventDefault()
 | 
				
			||||||
 | 
					    const formData = new FormData(event.currentTarget)
 | 
				
			||||||
 | 
					    const newBill = {
 | 
				
			||||||
 | 
					      id: bills.length + 1,
 | 
				
			||||||
 | 
					      name: formData.get('billName') as string,
 | 
				
			||||||
 | 
					      amount: Number(formData.get('amount')),
 | 
				
			||||||
 | 
					      dueDate: selectedDate as Date,
 | 
				
			||||||
 | 
					      category: formData.get('category') as string,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setBills([...bills, newBill])
 | 
				
			||||||
 | 
					    setIsDialogOpen(false)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getDayContent = (day: Date | undefined) => {
 | 
				
			||||||
 | 
					    if (!day) return null;
 | 
				
			||||||
 | 
					    const dayBills = bills.filter(bill => 
 | 
				
			||||||
 | 
					      bill.dueDate.getDate() === day.getDate() &&
 | 
				
			||||||
 | 
					      bill.dueDate.getMonth() === day.getMonth() &&
 | 
				
			||||||
 | 
					      bill.dueDate.getFullYear() === day.getFullYear()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    return dayBills.length > 0 ? (
 | 
				
			||||||
 | 
					      <div className="w-full h-full flex items-center justify-center">
 | 
				
			||||||
 | 
					        <div className="h-2 w-2 bg-primary rounded-full" />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    ) : null
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					            <CardTitle className="text-2xl">Bill Tracker</CardTitle>
 | 
				
			||||||
 | 
					            <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
 | 
				
			||||||
 | 
					              <DialogTrigger asChild>
 | 
				
			||||||
 | 
					                <Button>
 | 
				
			||||||
 | 
					                  <Plus className="mr-2 h-4 w-4" /> Add Bill
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </DialogTrigger>
 | 
				
			||||||
 | 
					              <DialogContent>
 | 
				
			||||||
 | 
					                <form onSubmit={handleAddBill}>
 | 
				
			||||||
 | 
					                  <DialogHeader>
 | 
				
			||||||
 | 
					                    <DialogTitle>Add New Bill</DialogTitle>
 | 
				
			||||||
 | 
					                    <DialogDescription>
 | 
				
			||||||
 | 
					                      Enter the details of the bill you want to track.
 | 
				
			||||||
 | 
					                    </DialogDescription>
 | 
				
			||||||
 | 
					                  </DialogHeader>
 | 
				
			||||||
 | 
					                  <div className="grid gap-4 py-4">
 | 
				
			||||||
 | 
					                    <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                      <Label htmlFor="billName" className="text-right">
 | 
				
			||||||
 | 
					                        Bill Name
 | 
				
			||||||
 | 
					                      </Label>
 | 
				
			||||||
 | 
					                      <Input id="billName" name="billName" className="col-span-3" required />
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                      <Label htmlFor="amount" className="text-right">
 | 
				
			||||||
 | 
					                        Amount
 | 
				
			||||||
 | 
					                      </Label>
 | 
				
			||||||
 | 
					                      <Input id="amount" name="amount" type="number" className="col-span-3" required />
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                      <Label htmlFor="category" className="text-right">
 | 
				
			||||||
 | 
					                        Category
 | 
				
			||||||
 | 
					                      </Label>
 | 
				
			||||||
 | 
					                      <Select name="category" required>
 | 
				
			||||||
 | 
					                        <SelectTrigger className="col-span-3">
 | 
				
			||||||
 | 
					                          <SelectValue placeholder="Select category" />
 | 
				
			||||||
 | 
					                        </SelectTrigger>
 | 
				
			||||||
 | 
					                        <SelectContent>
 | 
				
			||||||
 | 
					                          <SelectItem value="Utilities">Utilities</SelectItem>
 | 
				
			||||||
 | 
					                          <SelectItem value="Rent">Rent</SelectItem>
 | 
				
			||||||
 | 
					                          <SelectItem value="Insurance">Insurance</SelectItem>
 | 
				
			||||||
 | 
					                          <SelectItem value="Subscriptions">Subscriptions</SelectItem>
 | 
				
			||||||
 | 
					                          <SelectItem value="Other">Other</SelectItem>
 | 
				
			||||||
 | 
					                        </SelectContent>
 | 
				
			||||||
 | 
					                      </Select>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <DialogFooter>
 | 
				
			||||||
 | 
					                    <Button type="submit">Add Bill</Button>
 | 
				
			||||||
 | 
					                  </DialogFooter>
 | 
				
			||||||
 | 
					                </form>
 | 
				
			||||||
 | 
					              </DialogContent>
 | 
				
			||||||
 | 
					            </Dialog>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="flex space-x-4">
 | 
				
			||||||
 | 
					            <div className="flex-1">
 | 
				
			||||||
 | 
					              <Calendar
 | 
				
			||||||
 | 
					                mode="single"
 | 
				
			||||||
 | 
					                selected={selectedDate}
 | 
				
			||||||
 | 
					                onSelect={setSelectedDate}
 | 
				
			||||||
 | 
					                className="rounded-md border"
 | 
				
			||||||
 | 
					                components={{
 | 
				
			||||||
 | 
					                  DayContent: ({ date }) => getDayContent(date),
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div className="flex-1">
 | 
				
			||||||
 | 
					              <h3 className="text-lg font-semibold mb-4">Upcoming Bills</h3>
 | 
				
			||||||
 | 
					              <ul className="space-y-2">
 | 
				
			||||||
 | 
					                {bills.map((bill) => (
 | 
				
			||||||
 | 
					                  <li key={bill.id} className="flex justify-between items-center p-2 bg-muted rounded-md">
 | 
				
			||||||
 | 
					                    <div>
 | 
				
			||||||
 | 
					                      <p className="font-medium">{bill.name}</p>
 | 
				
			||||||
 | 
					                      <p className="text-sm text-muted-foreground">{bill.dueDate.toLocaleDateString()}</p>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div className="flex items-center">
 | 
				
			||||||
 | 
					                      <DollarSign className="h-4 w-4 mr-1 text-primary" />
 | 
				
			||||||
 | 
					                      <span className="font-semibold">{bill.amount.toFixed(2)}</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </li>
 | 
				
			||||||
 | 
					                ))}
 | 
				
			||||||
 | 
					              </ul>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										19
									
								
								src/app/account/billtracker/page.tsx.bak
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										19
									
								
								src/app/account/billtracker/page.tsx.bak
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					"use server"
 | 
				
			||||||
 | 
					import { auth } from "~/auth"
 | 
				
			||||||
 | 
					import BreadCrumbBillTracker from "~/components/portal/home/breadcrumb/BreadCrumbBillTracker"
 | 
				
			||||||
 | 
					import BillTrackerCalendar from "~/components/portal/billtracker/BillTrackerCalendar"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default async function HomePage() {
 | 
				
			||||||
 | 
					  const session = await auth()
 | 
				
			||||||
 | 
					  if (!session?.user) return <></>
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div className="w-2/3 flex flex-col p-6">
 | 
				
			||||||
 | 
					        <div className="flex flex-row">
 | 
				
			||||||
 | 
					          <div className="">
 | 
				
			||||||
 | 
					            <BreadCrumbBillTracker />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        < BillTrackerCalendar />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"
 | 
				
			||||||
 | 
					import { FileText, Download, Upload } from 'lucide-react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for documents
 | 
				
			||||||
 | 
					const documents = [
 | 
				
			||||||
 | 
					  { id: 1, name: 'Lease Agreement', type: 'PDF', size: '2.5 MB', date: '2023-01-15' },
 | 
				
			||||||
 | 
					  { id: 2, name: 'Move-in Checklist', type: 'DOCX', size: '1.2 MB', date: '2023-01-15' },
 | 
				
			||||||
 | 
					  { id: 3, name: 'Rent Payment Receipt - June 2023', type: 'PDF', size: '0.5 MB', date: '2023-06-01' },
 | 
				
			||||||
 | 
					  { id: 4, name: 'Property Rules and Regulations', type: 'PDF', size: '1.8 MB', date: '2023-01-15' },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function DocumentsPage() {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					            <CardTitle className="text-2xl">Documents</CardTitle>
 | 
				
			||||||
 | 
					            <Button>
 | 
				
			||||||
 | 
					              <Upload className="mr-2 h-4 w-4" /> Upload Document
 | 
				
			||||||
 | 
					            </Button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <Table>
 | 
				
			||||||
 | 
					            <TableHeader>
 | 
				
			||||||
 | 
					              <TableRow>
 | 
				
			||||||
 | 
					                <TableHead>Name</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Type</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Size</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Date</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Action</TableHead>
 | 
				
			||||||
 | 
					              </TableRow>
 | 
				
			||||||
 | 
					            </TableHeader>
 | 
				
			||||||
 | 
					            <TableBody>
 | 
				
			||||||
 | 
					              {documents.map((doc) => (
 | 
				
			||||||
 | 
					                <TableRow key={doc.id}>
 | 
				
			||||||
 | 
					                  <TableCell className="font-medium">
 | 
				
			||||||
 | 
					                    <div className="flex items-center">
 | 
				
			||||||
 | 
					                      <FileText className="mr-2 h-4 w-4" />
 | 
				
			||||||
 | 
					                      {doc.name}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{doc.type}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{doc.size}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{doc.date}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Button variant="ghost" size="sm">
 | 
				
			||||||
 | 
					                      <Download className="mr-2 h-4 w-4" /> Download
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					            </TableBody>
 | 
				
			||||||
 | 
					          </Table>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					'use client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useState } from 'react'
 | 
				
			||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Input } from "~/components/ui/input"
 | 
				
			||||||
 | 
					import { Avatar, AvatarFallback } from "~/components/ui/avatar"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for messages
 | 
				
			||||||
 | 
					const initialMessages = [
 | 
				
			||||||
 | 
					  { id: 1, sender: 'Property Manager', content: 'Your maintenance request has been received and scheduled for next Tuesday.', timestamp: '2023-06-20T10:30:00Z' },
 | 
				
			||||||
 | 
					  { id: 2, sender: 'Tenant', content: 'Thank you for the quick response. I'll make sure to be available on Tuesday.', timestamp: '2023-06-20T11:15:00Z' },
 | 
				
			||||||
 | 
					  { id: 3, sender: 'Property Manager', content: 'Great! The maintenance team will arrive between 9 AM and 12 PM. Please ensure they have access to the affected area.', timestamp: '2023-06-20T14:00:00Z' },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function MessagesPage() {
 | 
				
			||||||
 | 
					  const [messages, setMessages] = useState(initialMessages)
 | 
				
			||||||
 | 
					  const [newMessage, setNewMessage] = useState('')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleSendMessage = (e: React.FormEvent) => {
 | 
				
			||||||
 | 
					    e.preventDefault()
 | 
				
			||||||
 | 
					    if (newMessage.trim()) {
 | 
				
			||||||
 | 
					      const message = {
 | 
				
			||||||
 | 
					        id: messages.length + 1,
 | 
				
			||||||
 | 
					        sender: 'Tenant',
 | 
				
			||||||
 | 
					        content: newMessage,
 | 
				
			||||||
 | 
					        timestamp: new Date().toISOString(),
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      setMessages([...messages, message])
 | 
				
			||||||
 | 
					      setNewMessage('')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle className="text-2xl">Messages</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="space-y-4">
 | 
				
			||||||
 | 
					            {messages.map((message) => (
 | 
				
			||||||
 | 
					              <div key={message.id} className={`flex ${message.sender === 'Tenant' ? 'justify-end' : 'justify-start'}`}>
 | 
				
			||||||
 | 
					                <div className={`flex ${message.sender === 'Tenant' ? 'flex-row-reverse' : 'flex-row'} items-start space-x-2`}>
 | 
				
			||||||
 | 
					                  <Avatar className="w-10 h-10">
 | 
				
			||||||
 | 
					                    <AvatarFallback>{message.sender[0]}</AvatarFallback>
 | 
				
			||||||
 | 
					                  </Avatar>
 | 
				
			||||||
 | 
					                  <div className={`rounded-lg p-3 ${message.sender === 'Tenant' ? 'bg-primary text-primary-foreground' : 'bg-muted'}`}>
 | 
				
			||||||
 | 
					                    <p className="text-sm font-medium">{message.sender}</p>
 | 
				
			||||||
 | 
					                    <p className="mt-1">{message.content}</p>
 | 
				
			||||||
 | 
					                    <p className="mt-1 text-xs opacity-70">{new Date(message.timestamp).toLocaleString()}</p>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            ))}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <form onSubmit={handleSendMessage} className="mt-4 flex space-x-2">
 | 
				
			||||||
 | 
					            <Input
 | 
				
			||||||
 | 
					              value={newMessage}
 | 
				
			||||||
 | 
					              onChange={(e) => setNewMessage(e.target.value)}
 | 
				
			||||||
 | 
					              placeholder="Type your message..."
 | 
				
			||||||
 | 
					              className="flex-grow"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <Button type="submit">Send</Button>
 | 
				
			||||||
 | 
					          </form>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,80 @@
 | 
				
			|||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"
 | 
				
			||||||
 | 
					import { CreditCard, DollarSign } from 'lucide-react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This would typically come from your database
 | 
				
			||||||
 | 
					const paymentHistory = [
 | 
				
			||||||
 | 
					  { id: 1, date: '2023-06-01', amount: 1200, status: 'Paid' },
 | 
				
			||||||
 | 
					  { id: 2, date: '2023-05-01', amount: 1200, status: 'Paid' },
 | 
				
			||||||
 | 
					  { id: 3, date: '2023-04-01', amount: 1200, status: 'Paid' },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function PaymentsPage() {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle className="text-2xl">Current Balance</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="text-4xl font-bold">$1,200.00</div>
 | 
				
			||||||
 | 
					          <p className="text-muted-foreground mt-2">Due on July 1, 2023</p>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					        <CardFooter>
 | 
				
			||||||
 | 
					          <Button className="w-full">
 | 
				
			||||||
 | 
					            <DollarSign className="mr-2 h-4 w-4" /> Make a Payment
 | 
				
			||||||
 | 
					          </Button>
 | 
				
			||||||
 | 
					        </CardFooter>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Payment Methods</CardTitle>
 | 
				
			||||||
 | 
					          <CardDescription>Manage your payment methods</CardDescription>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="flex items-center justify-between p-4 border rounded-lg">
 | 
				
			||||||
 | 
					            <div className="flex items-center">
 | 
				
			||||||
 | 
					              <CreditCard className="mr-2 h-4 w-4" />
 | 
				
			||||||
 | 
					              <div>
 | 
				
			||||||
 | 
					                <p className="font-medium">Visa ending in 1234</p>
 | 
				
			||||||
 | 
					                <p className="text-sm text-muted-foreground">Expires 12/2025</p>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <Button variant="outline">Edit</Button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					        <CardFooter>
 | 
				
			||||||
 | 
					          <Button variant="outline" className="w-full">Add Payment Method</Button>
 | 
				
			||||||
 | 
					        </CardFooter>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Payment History</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <Table>
 | 
				
			||||||
 | 
					            <TableHeader>
 | 
				
			||||||
 | 
					              <TableRow>
 | 
				
			||||||
 | 
					                <TableHead>Date</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Amount</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Status</TableHead>
 | 
				
			||||||
 | 
					              </TableRow>
 | 
				
			||||||
 | 
					            </TableHeader>
 | 
				
			||||||
 | 
					            <TableBody>
 | 
				
			||||||
 | 
					              {paymentHistory.map((payment) => (
 | 
				
			||||||
 | 
					                <TableRow key={payment.id}>
 | 
				
			||||||
 | 
					                  <TableCell>{payment.date}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>${payment.amount.toFixed(2)}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{payment.status}</TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					            </TableBody>
 | 
				
			||||||
 | 
					          </Table>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					'use client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useState } from 'react'
 | 
				
			||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"
 | 
				
			||||||
 | 
					import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"
 | 
				
			||||||
 | 
					import { Input } from "~/components/ui/input"
 | 
				
			||||||
 | 
					import { Label } from "~/components/ui/label"
 | 
				
			||||||
 | 
					import { Textarea } from "~/components/ui/textarea"
 | 
				
			||||||
 | 
					import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"
 | 
				
			||||||
 | 
					import { PenToolIcon as Tool, Plus } from 'lucide-react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This would typically come from your database
 | 
				
			||||||
 | 
					const workOrders = [
 | 
				
			||||||
 | 
					  { id: 1, date: '2023-06-15', issue: 'Leaky faucet', status: 'In Progress' },
 | 
				
			||||||
 | 
					  { id: 2, date: '2023-06-10', issue: 'Broken AC', status: 'Scheduled' },
 | 
				
			||||||
 | 
					  { id: 3, date: '2023-05-28', issue: 'Clogged drain', status: 'Completed' },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function WorkOrdersPage() {
 | 
				
			||||||
 | 
					  const [isDialogOpen, setIsDialogOpen] = useState(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
 | 
				
			||||||
 | 
					    event.preventDefault()
 | 
				
			||||||
 | 
					    // Here you would typically send the form data to your server
 | 
				
			||||||
 | 
					    setIsDialogOpen(false)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					            <CardTitle className="text-2xl">Work Orders</CardTitle>
 | 
				
			||||||
 | 
					            <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
 | 
				
			||||||
 | 
					              <DialogTrigger asChild>
 | 
				
			||||||
 | 
					                <Button>
 | 
				
			||||||
 | 
					                  <Plus className="mr-2 h-4 w-4" /> New Work Order
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </DialogTrigger>
 | 
				
			||||||
 | 
					              <DialogContent>
 | 
				
			||||||
 | 
					                <form onSubmit={handleSubmit}>
 | 
				
			||||||
 | 
					                  <DialogHeader>
 | 
				
			||||||
 | 
					                    <DialogTitle>Create New Work Order</DialogTitle>
 | 
				
			||||||
 | 
					                    <DialogDescription>
 | 
				
			||||||
 | 
					                      Describe the issue you're experiencing. We'll get on it as soon as possible.
 | 
				
			||||||
 | 
					                    </DialogDescription>
 | 
				
			||||||
 | 
					                  </DialogHeader>
 | 
				
			||||||
 | 
					                  <div className="grid gap-4 py-4">
 | 
				
			||||||
 | 
					                    <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                      <Label htmlFor="issue" className="text-right">
 | 
				
			||||||
 | 
					                        Issue
 | 
				
			||||||
 | 
					                      </Label>
 | 
				
			||||||
 | 
					                      <Input id="issue" className="col-span-3" required />
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                      <Label htmlFor="location" className="text-right">
 | 
				
			||||||
 | 
					                        Location
 | 
				
			||||||
 | 
					                      </Label>
 | 
				
			||||||
 | 
					                      <Select required>
 | 
				
			||||||
 | 
					                        <SelectTrigger className="col-span-3">
 | 
				
			||||||
 | 
					                          <SelectValue placeholder="Select location" />
 | 
				
			||||||
 | 
					                        </SelectTrigger>
 | 
				
			||||||
 | 
					                        <SelectContent>
 | 
				
			||||||
 | 
					                          <SelectItem value="kitchen">Kitchen</SelectItem>
 | 
				
			||||||
 | 
					                          <SelectItem value="bathroom">Bathroom</SelectItem>
 | 
				
			||||||
 | 
					                          <SelectItem value="bedroom">Bedroom</SelectItem>
 | 
				
			||||||
 | 
					                          <SelectItem value="livingroom">Living Room</SelectItem>
 | 
				
			||||||
 | 
					                          <SelectItem value="other">Other</SelectItem>
 | 
				
			||||||
 | 
					                        </SelectContent>
 | 
				
			||||||
 | 
					                      </Select>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                      <Label htmlFor="description" className="text-right">
 | 
				
			||||||
 | 
					                        Description
 | 
				
			||||||
 | 
					                      </Label>
 | 
				
			||||||
 | 
					                      <Textarea id="description" className="col-span-3" required />
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <DialogFooter>
 | 
				
			||||||
 | 
					                    <Button type="submit">Submit Work Order</Button>
 | 
				
			||||||
 | 
					                  </DialogFooter>
 | 
				
			||||||
 | 
					                </form>
 | 
				
			||||||
 | 
					              </DialogContent>
 | 
				
			||||||
 | 
					            </Dialog>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <Table>
 | 
				
			||||||
 | 
					            <TableHeader>
 | 
				
			||||||
 | 
					              <TableRow>
 | 
				
			||||||
 | 
					                <TableHead>Date</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Issue</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Status</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Action</TableHead>
 | 
				
			||||||
 | 
					              </TableRow>
 | 
				
			||||||
 | 
					            </TableHeader>
 | 
				
			||||||
 | 
					            <TableBody>
 | 
				
			||||||
 | 
					              {workOrders.map((order) => (
 | 
				
			||||||
 | 
					                <TableRow key={order.id}>
 | 
				
			||||||
 | 
					                  <TableCell>{order.date}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{order.issue}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{order.status}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Button variant="outline" size="sm">View Details</Button>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					            </TableBody>
 | 
				
			||||||
 | 
					          </Table>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Maintenance Tips</CardTitle>
 | 
				
			||||||
 | 
					          <CardDescription>Keep your living space in top condition with these tips</CardDescription>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <ul className="list-disc pl-4 space-y-2">
 | 
				
			||||||
 | 
					            <li>Regularly clean or replace HVAC filters</li>
 | 
				
			||||||
 | 
					            <li>Check and clean gutters seasonally</li>
 | 
				
			||||||
 | 
					            <li>Test smoke and carbon monoxide detectors monthly</li>
 | 
				
			||||||
 | 
					            <li>Inspect plumbing fixtures for leaks</li>
 | 
				
			||||||
 | 
					          </ul>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					        <CardFooter>
 | 
				
			||||||
 | 
					          <Button variant="link" className="w-full">View More Tips</Button>
 | 
				
			||||||
 | 
					        </CardFooter>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										223
									
								
								src/app/admin/documents/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								src/app/admin/documents/page.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,223 @@
 | 
				
			|||||||
 | 
					'use client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useState } from 'react'
 | 
				
			||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Input } from "~/components/ui/input"
 | 
				
			||||||
 | 
					import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"
 | 
				
			||||||
 | 
					import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"
 | 
				
			||||||
 | 
					import { Label } from "~/components/ui/label"
 | 
				
			||||||
 | 
					import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"
 | 
				
			||||||
 | 
					import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"
 | 
				
			||||||
 | 
					import { Badge } from "~/components/ui/badge"
 | 
				
			||||||
 | 
					import { Search, Plus, FileText, Download, Trash2, Share2, Upload } from 'lucide-react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for documents
 | 
				
			||||||
 | 
					const documents = [
 | 
				
			||||||
 | 
					  { id: 1, name: "Lease Agreement - John Doe", type: "PDF", size: "2.5 MB", date: "2023-07-01", status: "Active" },
 | 
				
			||||||
 | 
					  { id: 2, name: "Rent Receipt - Jane Smith", type: "PDF", size: "1.2 MB", date: "2023-07-02", status: "Archived" },
 | 
				
			||||||
 | 
					  { id: 3, name: "Maintenance Report - Riverside Condos", type: "DOCX", size: "3.7 MB", date: "2023-07-03", status: "Active" },
 | 
				
			||||||
 | 
					  { id: 4, name: "Property Rules - Sunset Apartments", type: "PDF", size: "1.8 MB", date: "2023-07-04", status: "Active" },
 | 
				
			||||||
 | 
					  // Add more mock data as needed
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function DocumentsPage() {
 | 
				
			||||||
 | 
					  const [searchTerm, setSearchTerm] = useState("")
 | 
				
			||||||
 | 
					  const [selectedStatus, setSelectedStatus] = useState("All")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const filteredDocuments = documents.filter(doc => 
 | 
				
			||||||
 | 
					    doc.name.toLowerCase().includes(searchTerm.toLowerCase()) &&
 | 
				
			||||||
 | 
					    (selectedStatus === "All" || doc.status === selectedStatus)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					            <CardTitle className="text-2xl">Documents</CardTitle>
 | 
				
			||||||
 | 
					            <Dialog>
 | 
				
			||||||
 | 
					              <DialogTrigger asChild>
 | 
				
			||||||
 | 
					                <Button>
 | 
				
			||||||
 | 
					                  <Plus className="mr-2 h-4 w-4" /> Upload Document
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </DialogTrigger>
 | 
				
			||||||
 | 
					              <DialogContent className="sm:max-w-[425px]">
 | 
				
			||||||
 | 
					                <DialogHeader>
 | 
				
			||||||
 | 
					                  <DialogTitle>Upload New Document</DialogTitle>
 | 
				
			||||||
 | 
					                  <DialogDescription>
 | 
				
			||||||
 | 
					                    Upload a new document and assign it to tenants or properties.
 | 
				
			||||||
 | 
					                  </DialogDescription>
 | 
				
			||||||
 | 
					                </DialogHeader>
 | 
				
			||||||
 | 
					                <div className="grid gap-4 py-4">
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="document" className="text-right">
 | 
				
			||||||
 | 
					                      Document
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="document" type="file" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="name" className="text-right">
 | 
				
			||||||
 | 
					                      Name
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="name" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="assign" className="text-right">
 | 
				
			||||||
 | 
					                      Assign To
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Select>
 | 
				
			||||||
 | 
					                      <SelectTrigger className="col-span-3">
 | 
				
			||||||
 | 
					                        <SelectValue placeholder="Select recipients" />
 | 
				
			||||||
 | 
					                      </SelectTrigger>
 | 
				
			||||||
 | 
					                      <SelectContent>
 | 
				
			||||||
 | 
					                        <SelectItem value="all">All Tenants</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="sunset">Sunset Apartments</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="oakwood">Oakwood Residences</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="riverside">Riverside Condos</SelectItem>
 | 
				
			||||||
 | 
					                      </SelectContent>
 | 
				
			||||||
 | 
					                    </Select>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <DialogFooter>
 | 
				
			||||||
 | 
					                  <Button type="submit">Upload Document</Button>
 | 
				
			||||||
 | 
					                </DialogFooter>
 | 
				
			||||||
 | 
					              </DialogContent>
 | 
				
			||||||
 | 
					            </Dialog>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center mb-4">
 | 
				
			||||||
 | 
					            <div className="flex items-center space-x-2">
 | 
				
			||||||
 | 
					              <Input
 | 
				
			||||||
 | 
					                placeholder="Search documents..."
 | 
				
			||||||
 | 
					                value={searchTerm}
 | 
				
			||||||
 | 
					                onChange={(e) => setSearchTerm(e.target.value)}
 | 
				
			||||||
 | 
					                className="w-[300px]"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					              <Search className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <Select value={selectedStatus} onValueChange={setSelectedStatus}>
 | 
				
			||||||
 | 
					              <SelectTrigger className="w-[180px]">
 | 
				
			||||||
 | 
					                <SelectValue placeholder="Filter by status" />
 | 
				
			||||||
 | 
					              </SelectTrigger>
 | 
				
			||||||
 | 
					              <SelectContent>
 | 
				
			||||||
 | 
					                <SelectItem value="All">All Statuses</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Active">Active</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Archived">Archived</SelectItem>
 | 
				
			||||||
 | 
					              </SelectContent>
 | 
				
			||||||
 | 
					            </Select>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <Table>
 | 
				
			||||||
 | 
					            <TableHeader>
 | 
				
			||||||
 | 
					              <TableRow>
 | 
				
			||||||
 | 
					                <TableHead>Name</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Type</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Size</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Date</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Status</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Actions</TableHead>
 | 
				
			||||||
 | 
					              </TableRow>
 | 
				
			||||||
 | 
					            </TableHeader>
 | 
				
			||||||
 | 
					            <TableBody>
 | 
				
			||||||
 | 
					              {filteredDocuments.map((doc) => (
 | 
				
			||||||
 | 
					                <TableRow key={doc.id}>
 | 
				
			||||||
 | 
					                  <TableCell className="font-medium">{doc.name}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{doc.type}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{doc.size}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{doc.date}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Badge variant={doc.status === 'Active' ? 'default' : 'secondary'}>
 | 
				
			||||||
 | 
					                      {doc.status}
 | 
				
			||||||
 | 
					                    </Badge>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <div className="flex space-x-2">
 | 
				
			||||||
 | 
					                      <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                        <Download className="h-4 w-4" />
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                      <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                        <Share2 className="h-4 w-4" />
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                      <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                        <Trash2 className="h-4 w-4" />
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					            </TableBody>
 | 
				
			||||||
 | 
					          </Table>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Document Statistics</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <Tabs defaultValue="overview">
 | 
				
			||||||
 | 
					            <TabsList>
 | 
				
			||||||
 | 
					              <TabsTrigger value="overview">Overview</TabsTrigger>
 | 
				
			||||||
 | 
					              <TabsTrigger value="usage">Usage</TabsTrigger>
 | 
				
			||||||
 | 
					            </TabsList>
 | 
				
			||||||
 | 
					            <TabsContent value="overview">
 | 
				
			||||||
 | 
					              <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Total Documents
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <FileText className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">{documents.length}</div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Active Documents
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <FileText className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">
 | 
				
			||||||
 | 
					                      {documents.filter(doc => doc.status === 'Active').length}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Total Storage Used
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <Upload className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">9.2 MB</div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Most Common Type
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <FileText className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">PDF</div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					            <TabsContent value="usage">
 | 
				
			||||||
 | 
					              <p>Document usage statistics and charts will be displayed here.</p>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					          </Tabs>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										62
									
								
								src/app/admin/layout.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/app/admin/layout.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					import Link from 'next/link'
 | 
				
			||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Card, CardContent } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { CreditCard, FileText, MessageSquare, PenToolIcon as Tool, BarChart, Users, Home } from 'lucide-react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function AdminLayout({
 | 
				
			||||||
 | 
					  children,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
					  children: React.ReactNode
 | 
				
			||||||
 | 
					}) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="container mx-auto px-4 py-8">
 | 
				
			||||||
 | 
					      <h1 className="text-3xl font-bold mb-8 text-primary">Admin Dashboard</h1>
 | 
				
			||||||
 | 
					      <div className="flex flex-col md:flex-row gap-8">
 | 
				
			||||||
 | 
					        <Card className="w-full md:w-64 h-fit">
 | 
				
			||||||
 | 
					          <CardContent className="p-4">
 | 
				
			||||||
 | 
					            <nav className="space-y-2">
 | 
				
			||||||
 | 
					              <Link href="/admin" passHref>
 | 
				
			||||||
 | 
					                <Button variant="ghost" className="w-full justify-start">
 | 
				
			||||||
 | 
					                  <BarChart className="mr-2 h-4 w-4" /> Dashboard
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </Link>
 | 
				
			||||||
 | 
					              <Link href="/admin/tenants" passHref>
 | 
				
			||||||
 | 
					                <Button variant="ghost" className="w-full justify-start">
 | 
				
			||||||
 | 
					                  <Users className="mr-2 h-4 w-4" /> Tenants
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </Link>
 | 
				
			||||||
 | 
					              <Link href="/admin/properties" passHref>
 | 
				
			||||||
 | 
					                <Button variant="ghost" className="w-full justify-start">
 | 
				
			||||||
 | 
					                  <Home className="mr-2 h-4 w-4" /> Properties
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </Link>
 | 
				
			||||||
 | 
					              <Link href="/admin/payments" passHref>
 | 
				
			||||||
 | 
					                <Button variant="ghost" className="w-full justify-start">
 | 
				
			||||||
 | 
					                  <CreditCard className="mr-2 h-4 w-4" /> Payments
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </Link>
 | 
				
			||||||
 | 
					              <Link href="/admin/workorders" passHref>
 | 
				
			||||||
 | 
					                <Button variant="ghost" className="w-full justify-start">
 | 
				
			||||||
 | 
					                  <Tool className="mr-2 h-4 w-4" /> Work Orders
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </Link>
 | 
				
			||||||
 | 
					              <Link href="/admin/messages" passHref>
 | 
				
			||||||
 | 
					                <Button variant="ghost" className="w-full justify-start">
 | 
				
			||||||
 | 
					                  <MessageSquare className="mr-2 h-4 w-4" /> Messages
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </Link>
 | 
				
			||||||
 | 
					              <Link href="/admin/documents" passHref>
 | 
				
			||||||
 | 
					                <Button variant="ghost" className="w-full justify-start">
 | 
				
			||||||
 | 
					                  <FileText className="mr-2 h-4 w-4" /> Documents
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </Link>
 | 
				
			||||||
 | 
					            </nav>
 | 
				
			||||||
 | 
					          </CardContent>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					        <main className="flex-1">
 | 
				
			||||||
 | 
					          {children}
 | 
				
			||||||
 | 
					        </main>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										249
									
								
								src/app/admin/messages/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								src/app/admin/messages/page.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
				
			|||||||
 | 
					'use client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useState } from 'react'
 | 
				
			||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Input } from "~/components/ui/input"
 | 
				
			||||||
 | 
					import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"
 | 
				
			||||||
 | 
					import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"
 | 
				
			||||||
 | 
					import { Label } from "~/components/ui/label"
 | 
				
			||||||
 | 
					import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"
 | 
				
			||||||
 | 
					import { Textarea } from "~/components/ui/textarea"
 | 
				
			||||||
 | 
					import { Avatar, AvatarFallback, AvatarImage } from "~/components/ui/avatar"
 | 
				
			||||||
 | 
					import { Badge } from "~/components/ui/badge"
 | 
				
			||||||
 | 
					import { Search, Plus, MessageSquare, Users, ArrowUpRight } from 'lucide-react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for messages
 | 
				
			||||||
 | 
					const messages = [
 | 
				
			||||||
 | 
					  { id: 1, tenant: "John Doe", property: "Sunset Apartments, Apt 4B", subject: "Maintenance Request", date: "2023-07-05", status: "Unread" },
 | 
				
			||||||
 | 
					  { id: 2, tenant: "Jane Smith", property: "Oakwood Residences, Apt 2A", subject: "Rent Inquiry", date: "2023-07-04", status: "Read" },
 | 
				
			||||||
 | 
					  { id: 3, tenant: "Bob Johnson", property: "Riverside Condos, Apt 3C", subject: "Lease Renewal", date: "2023-07-03", status: "Replied" },
 | 
				
			||||||
 | 
					  { id: 4, tenant: "Alice Brown", property: "Sunset Apartments, Apt 2C", subject: "Noise Complaint", date: "2023-07-02", status: "Unread" },
 | 
				
			||||||
 | 
					  // Add more mock data as needed
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function MessagesPage() {
 | 
				
			||||||
 | 
					  const [searchTerm, setSearchTerm] = useState("")
 | 
				
			||||||
 | 
					  const [selectedStatus, setSelectedStatus] = useState("All")
 | 
				
			||||||
 | 
					  const [selectedMessage, setSelectedMessage] = useState<typeof messages[0] | null>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const filteredMessages = messages.filter(message => 
 | 
				
			||||||
 | 
					    (message.tenant.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
				
			||||||
 | 
					     message.property.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
				
			||||||
 | 
					     message.subject.toLowerCase().includes(searchTerm.toLowerCase())) &&
 | 
				
			||||||
 | 
					    (selectedStatus === "All" || message.status === selectedStatus)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					            <CardTitle className="text-2xl">Messages</CardTitle>
 | 
				
			||||||
 | 
					            <Dialog>
 | 
				
			||||||
 | 
					              <DialogTrigger asChild>
 | 
				
			||||||
 | 
					                <Button>
 | 
				
			||||||
 | 
					                  <Plus className="mr-2 h-4 w-4" /> New Message
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </DialogTrigger>
 | 
				
			||||||
 | 
					              <DialogContent className="sm:max-w-[425px]">
 | 
				
			||||||
 | 
					                <DialogHeader>
 | 
				
			||||||
 | 
					                  <DialogTitle>Send New Message</DialogTitle>
 | 
				
			||||||
 | 
					                  <DialogDescription>
 | 
				
			||||||
 | 
					                    Compose a new message to send to a tenant or multiple tenants.
 | 
				
			||||||
 | 
					                  </DialogDescription>
 | 
				
			||||||
 | 
					                </DialogHeader>
 | 
				
			||||||
 | 
					                <div className="grid gap-4 py-4">
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="recipients" className="text-right">
 | 
				
			||||||
 | 
					                      To
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Select>
 | 
				
			||||||
 | 
					                      <SelectTrigger className="col-span-3">
 | 
				
			||||||
 | 
					                        <SelectValue placeholder="Select recipients" />
 | 
				
			||||||
 | 
					                      </SelectTrigger>
 | 
				
			||||||
 | 
					                      <SelectContent>
 | 
				
			||||||
 | 
					                        <SelectItem value="all">All Tenants</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="sunset">Sunset Apartments</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="oakwood">Oakwood Residences</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="riverside">Riverside Condos</SelectItem>
 | 
				
			||||||
 | 
					                      </SelectContent>
 | 
				
			||||||
 | 
					                    </Select>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="subject" className="text-right">
 | 
				
			||||||
 | 
					                      Subject
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="subject" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="message" className="text-right">
 | 
				
			||||||
 | 
					                      Message
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Textarea id="message" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <DialogFooter>
 | 
				
			||||||
 | 
					                  <Button type="submit">Send Message</Button>
 | 
				
			||||||
 | 
					                </DialogFooter>
 | 
				
			||||||
 | 
					              </DialogContent>
 | 
				
			||||||
 | 
					            </Dialog>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center mb-4">
 | 
				
			||||||
 | 
					            <div className="flex items-center space-x-2">
 | 
				
			||||||
 | 
					              <Input
 | 
				
			||||||
 | 
					                placeholder="Search messages..."
 | 
				
			||||||
 | 
					                value={searchTerm}
 | 
				
			||||||
 | 
					                onChange={(e) => setSearchTerm(e.target.value)}
 | 
				
			||||||
 | 
					                className="w-[300px]"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					              <Search className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <Select value={selectedStatus} onValueChange={setSelectedStatus}>
 | 
				
			||||||
 | 
					              <SelectTrigger className="w-[180px]">
 | 
				
			||||||
 | 
					                <SelectValue placeholder="Filter by status" />
 | 
				
			||||||
 | 
					              </SelectTrigger>
 | 
				
			||||||
 | 
					              <SelectContent>
 | 
				
			||||||
 | 
					                <SelectItem value="All">All Statuses</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Unread">Unread</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Read">Read</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Replied">Replied</SelectItem>
 | 
				
			||||||
 | 
					              </SelectContent>
 | 
				
			||||||
 | 
					            </Select>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <Table>
 | 
				
			||||||
 | 
					            <TableHeader>
 | 
				
			||||||
 | 
					              <TableRow>
 | 
				
			||||||
 | 
					                <TableHead>Tenant</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Property</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Subject</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Date</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Status</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Actions</TableHead>
 | 
				
			||||||
 | 
					              </TableRow>
 | 
				
			||||||
 | 
					            </TableHeader>
 | 
				
			||||||
 | 
					            <TableBody>
 | 
				
			||||||
 | 
					              {filteredMessages.map((message) => (
 | 
				
			||||||
 | 
					                <TableRow key={message.id}>
 | 
				
			||||||
 | 
					                  <TableCell>{message.tenant}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{message.property}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{message.subject}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{message.date}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Badge variant={
 | 
				
			||||||
 | 
					                      message.status === 'Unread' ? 'default' :
 | 
				
			||||||
 | 
					                      message.status === 'Read' ? 'secondary' :
 | 
				
			||||||
 | 
					                      'outline'
 | 
				
			||||||
 | 
					                    }>
 | 
				
			||||||
 | 
					                      {message.status}
 | 
				
			||||||
 | 
					                    </Badge>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Button variant="ghost" size="icon" onClick={() => setSelectedMessage(message)}>
 | 
				
			||||||
 | 
					                      <MessageSquare className="h-4 w-4" />
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					            </TableBody>
 | 
				
			||||||
 | 
					          </Table>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {selectedMessage && (
 | 
				
			||||||
 | 
					        <Card>
 | 
				
			||||||
 | 
					          <CardHeader>
 | 
				
			||||||
 | 
					            <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					              <CardTitle className="text-2xl">Message Details</CardTitle>
 | 
				
			||||||
 | 
					              <Button variant="outline" onClick={() => setSelectedMessage(null)}>Close</Button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </CardHeader>
 | 
				
			||||||
 | 
					          <CardContent>
 | 
				
			||||||
 | 
					            <div className="space-y-4">
 | 
				
			||||||
 | 
					              <div className="flex items-center space-x-4">
 | 
				
			||||||
 | 
					                <Avatar>
 | 
				
			||||||
 | 
					                  <AvatarImage src="/placeholder-avatar.jpg" alt={selectedMessage.tenant} />
 | 
				
			||||||
 | 
					                  <AvatarFallback>{selectedMessage.tenant[0]}</AvatarFallback>
 | 
				
			||||||
 | 
					                </Avatar>
 | 
				
			||||||
 | 
					                <div>
 | 
				
			||||||
 | 
					                  <h3 className="font-semibold">{selectedMessage.tenant}</h3>
 | 
				
			||||||
 | 
					                  <p className="text-sm text-muted-foreground">{selectedMessage.property}</p>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					              <div>
 | 
				
			||||||
 | 
					                <h4 className="font-semibold">Subject: {selectedMessage.subject}</h4>
 | 
				
			||||||
 | 
					                <p className="text-sm text-muted-foreground">Received on {selectedMessage.date}</p>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					              <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
 | 
				
			||||||
 | 
					              <Textarea placeholder="Type your reply here..." className="mt-4" />
 | 
				
			||||||
 | 
					              <div className="flex justify-end space-x-2">
 | 
				
			||||||
 | 
					                <Button variant="outline">Save Draft</Button>
 | 
				
			||||||
 | 
					                <Button>Send Reply</Button>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </CardContent>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Message Statistics</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
 | 
				
			||||||
 | 
					            <Card>
 | 
				
			||||||
 | 
					              <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                  Total Messages
 | 
				
			||||||
 | 
					                </CardTitle>
 | 
				
			||||||
 | 
					                <MessageSquare className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					              </CardHeader>
 | 
				
			||||||
 | 
					              <CardContent>
 | 
				
			||||||
 | 
					                <div className="text-2xl font-bold">{messages.length}</div>
 | 
				
			||||||
 | 
					              </CardContent>
 | 
				
			||||||
 | 
					            </Card>
 | 
				
			||||||
 | 
					            <Card>
 | 
				
			||||||
 | 
					              <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                  Unread Messages
 | 
				
			||||||
 | 
					                </CardTitle>
 | 
				
			||||||
 | 
					                <ArrowUpRight className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					              </CardHeader>
 | 
				
			||||||
 | 
					              <CardContent>
 | 
				
			||||||
 | 
					                <div className="text-2xl font-bold">
 | 
				
			||||||
 | 
					                  {messages.filter(m => m.status === 'Unread').length}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </CardContent>
 | 
				
			||||||
 | 
					            </Card>
 | 
				
			||||||
 | 
					            <Card>
 | 
				
			||||||
 | 
					              <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                  Response Rate
 | 
				
			||||||
 | 
					                </CardTitle>
 | 
				
			||||||
 | 
					                <Users className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					              </CardHeader>
 | 
				
			||||||
 | 
					              <CardContent>
 | 
				
			||||||
 | 
					                <div className="text-2xl font-bold">
 | 
				
			||||||
 | 
					                  {Math.round((messages.filter(m => m.status === 'Replied').length / messages.length) * 100)}%
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </CardContent>
 | 
				
			||||||
 | 
					            </Card>
 | 
				
			||||||
 | 
					            <Card>
 | 
				
			||||||
 | 
					              <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                  Avg. Response Time
 | 
				
			||||||
 | 
					                </CardTitle>
 | 
				
			||||||
 | 
					                <MessageSquare className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					              </CardHeader>
 | 
				
			||||||
 | 
					              <CardContent>
 | 
				
			||||||
 | 
					                <div className="text-2xl font-bold">4.2 hours</div>
 | 
				
			||||||
 | 
					              </CardContent>
 | 
				
			||||||
 | 
					            </Card>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										174
									
								
								src/app/admin/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/app/admin/page.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,174 @@
 | 
				
			|||||||
 | 
					'use client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { ChartContainer, ChartTooltip, ChartTooltipContent } from "~/components/ui/chart"
 | 
				
			||||||
 | 
					import { Bar, BarChart, Line, LineChart, ResponsiveContainer, XAxis, YAxis } from "recharts"
 | 
				
			||||||
 | 
					import { CreditCard, Users, Home, Wrench, AlertTriangle } from 'lucide-react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for charts
 | 
				
			||||||
 | 
					const rentData = [
 | 
				
			||||||
 | 
					  { month: "Jan", collected: 95 },
 | 
				
			||||||
 | 
					  { month: "Feb", collected: 98 },
 | 
				
			||||||
 | 
					  { month: "Mar", collected: 92 },
 | 
				
			||||||
 | 
					  { month: "Apr", collected: 97 },
 | 
				
			||||||
 | 
					  { month: "May", collected: 99 },
 | 
				
			||||||
 | 
					  { month: "Jun", collected: 94 },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const occupancyData = [
 | 
				
			||||||
 | 
					  { month: "Jan", rate: 92 },
 | 
				
			||||||
 | 
					  { month: "Feb", rate: 94 },
 | 
				
			||||||
 | 
					  { month: "Mar", rate: 96 },
 | 
				
			||||||
 | 
					  { month: "Apr", rate: 95 },
 | 
				
			||||||
 | 
					  { month: "May", rate: 97 },
 | 
				
			||||||
 | 
					  { month: "Jun", rate: 98 },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function AdminDashboard() {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
 | 
				
			||||||
 | 
					        <Card>
 | 
				
			||||||
 | 
					          <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					            <CardTitle className="text-sm font-medium">Total Revenue</CardTitle>
 | 
				
			||||||
 | 
					            <CreditCard className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					          </CardHeader>
 | 
				
			||||||
 | 
					          <CardContent>
 | 
				
			||||||
 | 
					            <div className="text-2xl font-bold">$24,560</div>
 | 
				
			||||||
 | 
					            <p className="text-xs text-muted-foreground">+20.1% from last month</p>
 | 
				
			||||||
 | 
					          </CardContent>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					        <Card>
 | 
				
			||||||
 | 
					          <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					            <CardTitle className="text-sm font-medium">Occupancy Rate</CardTitle>
 | 
				
			||||||
 | 
					            <Users className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					          </CardHeader>
 | 
				
			||||||
 | 
					          <CardContent>
 | 
				
			||||||
 | 
					            <div className="text-2xl font-bold">98%</div>
 | 
				
			||||||
 | 
					            <p className="text-xs text-muted-foreground">+2% from last month</p>
 | 
				
			||||||
 | 
					          </CardContent>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					        <Card>
 | 
				
			||||||
 | 
					          <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					            <CardTitle className="text-sm font-medium">Total Properties</CardTitle>
 | 
				
			||||||
 | 
					            <Home className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					          </CardHeader>
 | 
				
			||||||
 | 
					          <CardContent>
 | 
				
			||||||
 | 
					            <div className="text-2xl font-bold">45</div>
 | 
				
			||||||
 | 
					            <p className="text-xs text-muted-foreground">+3 new this month</p>
 | 
				
			||||||
 | 
					          </CardContent>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					        <Card>
 | 
				
			||||||
 | 
					          <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					            <CardTitle className="text-sm font-medium">Active Work Orders</CardTitle>
 | 
				
			||||||
 | 
					            <Wrench className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					          </CardHeader>
 | 
				
			||||||
 | 
					          <CardContent>
 | 
				
			||||||
 | 
					            <div className="text-2xl font-bold">12</div>
 | 
				
			||||||
 | 
					            <p className="text-xs text-muted-foreground">-5 from last week</p>
 | 
				
			||||||
 | 
					          </CardContent>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <div className="grid gap-4 md:grid-cols-2">
 | 
				
			||||||
 | 
					        <Card>
 | 
				
			||||||
 | 
					          <CardHeader>
 | 
				
			||||||
 | 
					            <CardTitle>Rent Collection Rate</CardTitle>
 | 
				
			||||||
 | 
					          </CardHeader>
 | 
				
			||||||
 | 
					          <CardContent>
 | 
				
			||||||
 | 
					            <ChartContainer config={{
 | 
				
			||||||
 | 
					              collected: {
 | 
				
			||||||
 | 
					                label: "Collected",
 | 
				
			||||||
 | 
					                color: "hsl(var(--chart-1))",
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            }} className="h-[200px]">
 | 
				
			||||||
 | 
					              <ResponsiveContainer width="100%" height="100%">
 | 
				
			||||||
 | 
					                <BarChart data={rentData}>
 | 
				
			||||||
 | 
					                  <XAxis dataKey="month" />
 | 
				
			||||||
 | 
					                  <YAxis />
 | 
				
			||||||
 | 
					                  <ChartTooltip content={<ChartTooltipContent />} />
 | 
				
			||||||
 | 
					                  <Bar dataKey="collected" fill="var(--color-collected)" radius={[4, 4, 0, 0]} />
 | 
				
			||||||
 | 
					                </BarChart>
 | 
				
			||||||
 | 
					              </ResponsiveContainer>
 | 
				
			||||||
 | 
					            </ChartContainer>
 | 
				
			||||||
 | 
					          </CardContent>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					        <Card>
 | 
				
			||||||
 | 
					          <CardHeader>
 | 
				
			||||||
 | 
					            <CardTitle>Occupancy Trend</CardTitle>
 | 
				
			||||||
 | 
					          </CardHeader>
 | 
				
			||||||
 | 
					          <CardContent>
 | 
				
			||||||
 | 
					            <ChartContainer config={{
 | 
				
			||||||
 | 
					              rate: {
 | 
				
			||||||
 | 
					                label: "Occupancy Rate",
 | 
				
			||||||
 | 
					                color: "hsl(var(--chart-2))",
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            }} className="h-[200px]">
 | 
				
			||||||
 | 
					              <ResponsiveContainer width="100%" height="100%">
 | 
				
			||||||
 | 
					                <LineChart data={occupancyData}>
 | 
				
			||||||
 | 
					                  <XAxis dataKey="month" />
 | 
				
			||||||
 | 
					                  <YAxis />
 | 
				
			||||||
 | 
					                  <ChartTooltip content={<ChartTooltipContent />} />
 | 
				
			||||||
 | 
					                  <Line type="monotone" dataKey="rate" stroke="var(--color-rate)" strokeWidth={2} />
 | 
				
			||||||
 | 
					                </LineChart>
 | 
				
			||||||
 | 
					              </ResponsiveContainer>
 | 
				
			||||||
 | 
					            </ChartContainer>
 | 
				
			||||||
 | 
					          </CardContent>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Recent Activities</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="space-y-4">
 | 
				
			||||||
 | 
					            <div className="flex items-center">
 | 
				
			||||||
 | 
					              <AlertTriangle className="h-4 w-4 text-yellow-500 mr-2" />
 | 
				
			||||||
 | 
					              <span className="text-sm">New work order: Leaky faucet at 123 Main St, Apt 4B</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div className="flex items-center">
 | 
				
			||||||
 | 
					              <Users className="h-4 w-4 text-green-500 mr-2" />
 | 
				
			||||||
 | 
					              <span className="text-sm">New tenant: John Doe moved into 456 Elm St, Apt 2A</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div className="flex items-center">
 | 
				
			||||||
 | 
					              <CreditCard className="h-4 w-4 text-blue-500 mr-2" />
 | 
				
			||||||
 | 
					              <span className="text-sm">Rent payment received: $1,200 from Jane Smith, 789 Oak Rd, Apt 3C</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Upcoming Lease Renewals</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="space-y-4">
 | 
				
			||||||
 | 
					            <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					              <div>
 | 
				
			||||||
 | 
					                <p className="font-medium">Sarah Johnson</p>
 | 
				
			||||||
 | 
					                <p className="text-sm text-muted-foreground">123 Main St, Apt 2B</p>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					              <div className="text-right">
 | 
				
			||||||
 | 
					                <p className="font-medium">Expires in 30 days</p>
 | 
				
			||||||
 | 
					                <Button size="sm">Send Renewal</Button>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					              <div>
 | 
				
			||||||
 | 
					                <p className="font-medium">Michael Brown</p>
 | 
				
			||||||
 | 
					                <p className="text-sm text-muted-foreground">456 Elm St, Apt 1A</p>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					              <div className="text-right">
 | 
				
			||||||
 | 
					                <p className="font-medium">Expires in 45 days</p>
 | 
				
			||||||
 | 
					                <Button size="sm">Send Renewal</Button>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										269
									
								
								src/app/admin/payments/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								src/app/admin/payments/page.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,269 @@
 | 
				
			|||||||
 | 
					'use client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useState } from 'react'
 | 
				
			||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Input } from "~/components/ui/input"
 | 
				
			||||||
 | 
					import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"
 | 
				
			||||||
 | 
					import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"
 | 
				
			||||||
 | 
					import { Label } from "~/components/ui/label"
 | 
				
			||||||
 | 
					import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"
 | 
				
			||||||
 | 
					import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"
 | 
				
			||||||
 | 
					import { Badge } from "~/components/ui/badge"
 | 
				
			||||||
 | 
					import { Search, Plus, FileText, DollarSign, CreditCard, Calendar } from 'lucide-react'
 | 
				
			||||||
 | 
					import { ChartContainer, ChartTooltip, ChartTooltipContent } from "~/components/ui/chart"
 | 
				
			||||||
 | 
					import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis } from "recharts"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for payments
 | 
				
			||||||
 | 
					const payments = [
 | 
				
			||||||
 | 
					  { id: 1, tenant: "John Doe", property: "Sunset Apartments, Apt 4B", amount: 1200, date: "2023-07-01", status: "Paid" },
 | 
				
			||||||
 | 
					  { id: 2, tenant: "Jane Smith", property: "Oakwood Residences, Apt 2A", amount: 1500, date: "2023-07-02", status: "Paid" },
 | 
				
			||||||
 | 
					  { id: 3, tenant: "Bob Johnson", property: "Riverside Condos, Apt 3C", amount: 1800, date: "2023-07-05", status: "Pending" },
 | 
				
			||||||
 | 
					  { id: 4, tenant: "Alice Brown", property: "Sunset Apartments, Apt 2C", amount: 1100, date: "2023-07-03", status: "Late" },
 | 
				
			||||||
 | 
					  // Add more mock data as needed
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for payment chart
 | 
				
			||||||
 | 
					const paymentChartData = [
 | 
				
			||||||
 | 
					  { month: "Jan", amount: 45000 },
 | 
				
			||||||
 | 
					  { month: "Feb", amount: 42000 },
 | 
				
			||||||
 | 
					  { month: "Mar", amount: 47000 },
 | 
				
			||||||
 | 
					  { month: "Apr", amount: 44000 },
 | 
				
			||||||
 | 
					  { month: "May", amount: 46000 },
 | 
				
			||||||
 | 
					  { month: "Jun", amount: 48000 },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function PaymentsPage() {
 | 
				
			||||||
 | 
					  const [searchTerm, setSearchTerm] = useState("")
 | 
				
			||||||
 | 
					  const [selectedStatus, setSelectedStatus] = useState("All")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const filteredPayments = payments.filter(payment => 
 | 
				
			||||||
 | 
					    (payment.tenant.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
				
			||||||
 | 
					     payment.property.toLowerCase().includes(searchTerm.toLowerCase())) &&
 | 
				
			||||||
 | 
					    (selectedStatus === "All" || payment.status === selectedStatus)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					            <CardTitle className="text-2xl">Payments</CardTitle>
 | 
				
			||||||
 | 
					            <Dialog>
 | 
				
			||||||
 | 
					              <DialogTrigger asChild>
 | 
				
			||||||
 | 
					                <Button>
 | 
				
			||||||
 | 
					                  <Plus className="mr-2 h-4 w-4" /> Record Payment
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </DialogTrigger>
 | 
				
			||||||
 | 
					              <DialogContent>
 | 
				
			||||||
 | 
					                <DialogHeader>
 | 
				
			||||||
 | 
					                  <DialogTitle>Record New Payment</DialogTitle>
 | 
				
			||||||
 | 
					                  <DialogDescription>
 | 
				
			||||||
 | 
					                    Enter the details of the new payment. Click save when you're done.
 | 
				
			||||||
 | 
					                  </DialogDescription>
 | 
				
			||||||
 | 
					                </DialogHeader>
 | 
				
			||||||
 | 
					                <div className="grid gap-4 py-4">
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="tenant" className="text-right">
 | 
				
			||||||
 | 
					                      Tenant
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Select>
 | 
				
			||||||
 | 
					                      <SelectTrigger className="col-span-3">
 | 
				
			||||||
 | 
					                        <SelectValue placeholder="Select tenant" />
 | 
				
			||||||
 | 
					                      </SelectTrigger>
 | 
				
			||||||
 | 
					                      <SelectContent>
 | 
				
			||||||
 | 
					                        <SelectItem value="john-doe">John Doe</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="jane-smith">Jane Smith</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="bob-johnson">Bob Johnson</SelectItem>
 | 
				
			||||||
 | 
					                      </SelectContent>
 | 
				
			||||||
 | 
					                    </Select>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="amount" className="text-right">
 | 
				
			||||||
 | 
					                      Amount
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="amount" type="number" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="date" className="text-right">
 | 
				
			||||||
 | 
					                      Date
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="date" type="date" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="method" className="text-right">
 | 
				
			||||||
 | 
					                      Payment Method
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Select>
 | 
				
			||||||
 | 
					                      <SelectTrigger className="col-span-3">
 | 
				
			||||||
 | 
					                        <SelectValue placeholder="Select method" />
 | 
				
			||||||
 | 
					                      </SelectTrigger>
 | 
				
			||||||
 | 
					                      <SelectContent>
 | 
				
			||||||
 | 
					                        <SelectItem value="credit-card">Credit Card</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="bank-transfer">Bank Transfer</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="cash">Cash</SelectItem>
 | 
				
			||||||
 | 
					                      </SelectContent>
 | 
				
			||||||
 | 
					                    </Select>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <DialogFooter>
 | 
				
			||||||
 | 
					                  <Button type="submit">Save Payment</Button>
 | 
				
			||||||
 | 
					                </DialogFooter>
 | 
				
			||||||
 | 
					              </DialogContent>
 | 
				
			||||||
 | 
					            </Dialog>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center mb-4">
 | 
				
			||||||
 | 
					            <div className="flex items-center space-x-2">
 | 
				
			||||||
 | 
					              <Input
 | 
				
			||||||
 | 
					                placeholder="Search payments..."
 | 
				
			||||||
 | 
					                value={searchTerm}
 | 
				
			||||||
 | 
					                onChange={(e) => setSearchTerm(e.target.value)}
 | 
				
			||||||
 | 
					                className="w-[300px]"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					              <Search className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <Select value={selectedStatus} onValueChange={setSelectedStatus}>
 | 
				
			||||||
 | 
					              <SelectTrigger className="w-[180px]">
 | 
				
			||||||
 | 
					                <SelectValue placeholder="Filter by status" />
 | 
				
			||||||
 | 
					              </SelectTrigger>
 | 
				
			||||||
 | 
					              <SelectContent>
 | 
				
			||||||
 | 
					                <SelectItem value="All">All Statuses</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Paid">Paid</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Pending">Pending</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Late">Late</SelectItem>
 | 
				
			||||||
 | 
					              </SelectContent>
 | 
				
			||||||
 | 
					            </Select>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <Table>
 | 
				
			||||||
 | 
					            <TableHeader>
 | 
				
			||||||
 | 
					              <TableRow>
 | 
				
			||||||
 | 
					                <TableHead>Tenant</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Property</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Amount</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Date</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Status</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Actions</TableHead>
 | 
				
			||||||
 | 
					              </TableRow>
 | 
				
			||||||
 | 
					            </TableHeader>
 | 
				
			||||||
 | 
					            <TableBody>
 | 
				
			||||||
 | 
					              {filteredPayments.map((payment) => (
 | 
				
			||||||
 | 
					                <TableRow key={payment.id}>
 | 
				
			||||||
 | 
					                  <TableCell>{payment.tenant}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{payment.property}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>${payment.amount}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{payment.date}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Badge variant={
 | 
				
			||||||
 | 
					                      payment.status === 'Paid' ? 'default' :
 | 
				
			||||||
 | 
					                      payment.status === 'Pending' ? 'secondary' :
 | 
				
			||||||
 | 
					                      'destructive'
 | 
				
			||||||
 | 
					                    }>
 | 
				
			||||||
 | 
					                      {payment.status}
 | 
				
			||||||
 | 
					                    </Badge>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                      <FileText className="h-4 w-4" />
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					            </TableBody>
 | 
				
			||||||
 | 
					          </Table>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Payment Statistics</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <Tabs defaultValue="overview">
 | 
				
			||||||
 | 
					            <TabsList>
 | 
				
			||||||
 | 
					              <TabsTrigger value="overview">Overview</TabsTrigger>
 | 
				
			||||||
 | 
					              <TabsTrigger value="trends">Trends</TabsTrigger>
 | 
				
			||||||
 | 
					            </TabsList>
 | 
				
			||||||
 | 
					            <TabsContent value="overview">
 | 
				
			||||||
 | 
					              <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Total Collected
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <DollarSign className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">$45,600</div>
 | 
				
			||||||
 | 
					                    <p className="text-xs text-muted-foreground">+20.1% from last month</p>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Pending Payments
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <Calendar className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">$3,800</div>
 | 
				
			||||||
 | 
					                    <p className="text-xs text-muted-foreground">5 payments pending</p>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Late Payments
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <CreditCard className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">$1,200</div>
 | 
				
			||||||
 | 
					                    <p className="text-xs text-muted-foreground">2 payments overdue</p>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Collection Rate
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <DollarSign className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">98%</div>
 | 
				
			||||||
 | 
					                    <p className="text-xs text-muted-foreground">+2% from last month</p>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					            <TabsContent value="trends">
 | 
				
			||||||
 | 
					              <Card>
 | 
				
			||||||
 | 
					                <CardHeader>
 | 
				
			||||||
 | 
					                  <CardTitle>Monthly Payment Trends</CardTitle>
 | 
				
			||||||
 | 
					                </CardHeader>
 | 
				
			||||||
 | 
					                <CardContent>
 | 
				
			||||||
 | 
					                  <ChartContainer config={{
 | 
				
			||||||
 | 
					                    amount: {
 | 
				
			||||||
 | 
					                      label: "Amount",
 | 
				
			||||||
 | 
					                      color: "hsl(var(--chart-1))",
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  }} className="h-[300px]">
 | 
				
			||||||
 | 
					                    <ResponsiveContainer width="100%" height="100%">
 | 
				
			||||||
 | 
					                      <BarChart data={paymentChartData}>
 | 
				
			||||||
 | 
					                        <XAxis dataKey="month" />
 | 
				
			||||||
 | 
					                        <YAxis />
 | 
				
			||||||
 | 
					                        <ChartTooltip content={<ChartTooltipContent />} />
 | 
				
			||||||
 | 
					                        <Bar dataKey="amount" fill="var(--color-amount)" radius={[4, 4, 0, 0]} />
 | 
				
			||||||
 | 
					                      </BarChart>
 | 
				
			||||||
 | 
					                    </ResponsiveContainer>
 | 
				
			||||||
 | 
					                  </ChartContainer>
 | 
				
			||||||
 | 
					                </CardContent>
 | 
				
			||||||
 | 
					              </Card>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					          </Tabs>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										218
									
								
								src/app/admin/properties/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								src/app/admin/properties/page.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,218 @@
 | 
				
			|||||||
 | 
					'use client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useState } from 'react'
 | 
				
			||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Input } from "~/components/ui/input"
 | 
				
			||||||
 | 
					import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"
 | 
				
			||||||
 | 
					import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"
 | 
				
			||||||
 | 
					import { Label } from "~/components/ui/label"
 | 
				
			||||||
 | 
					import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"
 | 
				
			||||||
 | 
					import { Badge } from "~/components/ui/badge"
 | 
				
			||||||
 | 
					import { Search, Plus, Edit, Trash2, Home, DollarSign, Users } from 'lucide-react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for properties
 | 
				
			||||||
 | 
					const properties = [
 | 
				
			||||||
 | 
					  { id: 1, name: "Sunset Apartments", address: "123 Main St", units: 20, occupancy: 18, rentRange: "$1000 - $1500" },
 | 
				
			||||||
 | 
					  { id: 2, name: "Oakwood Residences", address: "456 Elm St", units: 15, occupancy: 14, rentRange: "$1200 - $1800" },
 | 
				
			||||||
 | 
					  { id: 3, name: "Riverside Condos", address: "789 Oak Rd", units: 30, occupancy: 28, rentRange: "$1500 - $2200" },
 | 
				
			||||||
 | 
					  // Add more mock data as needed
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function PropertiesPage() {
 | 
				
			||||||
 | 
					  const [searchTerm, setSearchTerm] = useState("")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const filteredProperties = properties.filter(property => 
 | 
				
			||||||
 | 
					    property.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
				
			||||||
 | 
					    property.address.toLowerCase().includes(searchTerm.toLowerCase())
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					            <CardTitle className="text-2xl">Properties</CardTitle>
 | 
				
			||||||
 | 
					            <Dialog>
 | 
				
			||||||
 | 
					              <DialogTrigger asChild>
 | 
				
			||||||
 | 
					                <Button>
 | 
				
			||||||
 | 
					                  <Plus className="mr-2 h-4 w-4" /> Add Property
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </DialogTrigger>
 | 
				
			||||||
 | 
					              <DialogContent>
 | 
				
			||||||
 | 
					                <DialogHeader>
 | 
				
			||||||
 | 
					                  <DialogTitle>Add New Property</DialogTitle>
 | 
				
			||||||
 | 
					                  <DialogDescription>
 | 
				
			||||||
 | 
					                    Enter the details of the new property. Click save when you're done.
 | 
				
			||||||
 | 
					                  </DialogDescription>
 | 
				
			||||||
 | 
					                </DialogHeader>
 | 
				
			||||||
 | 
					                <div className="grid gap-4 py-4">
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="name" className="text-right">
 | 
				
			||||||
 | 
					                      Name
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="name" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="address" className="text-right">
 | 
				
			||||||
 | 
					                      Address
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="address" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="units" className="text-right">
 | 
				
			||||||
 | 
					                      Total Units
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="units" type="number" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="rentMin" className="text-right">
 | 
				
			||||||
 | 
					                      Min Rent
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="rentMin" type="number" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="rentMax" className="text-right">
 | 
				
			||||||
 | 
					                      Max Rent
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="rentMax" type="number" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <DialogFooter>
 | 
				
			||||||
 | 
					                  <Button type="submit">Save Property</Button>
 | 
				
			||||||
 | 
					                </DialogFooter>
 | 
				
			||||||
 | 
					              </DialogContent>
 | 
				
			||||||
 | 
					            </Dialog>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center mb-4">
 | 
				
			||||||
 | 
					            <div className="flex items-center space-x-2">
 | 
				
			||||||
 | 
					              <Input
 | 
				
			||||||
 | 
					                placeholder="Search properties..."placeholder="Search properties..."
 | 
				
			||||||
 | 
					                value={searchTerm}
 | 
				
			||||||
 | 
					                onChange={(e) => setSearchTerm(e.target.value)}
 | 
				
			||||||
 | 
					                className="w-[300px]"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					              <Search className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <Table>
 | 
				
			||||||
 | 
					            <TableHeader>
 | 
				
			||||||
 | 
					              <TableRow>
 | 
				
			||||||
 | 
					                <TableHead>Name</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Address</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Units</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Occupancy</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Rent Range</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Actions</TableHead>
 | 
				
			||||||
 | 
					              </TableRow>
 | 
				
			||||||
 | 
					            </TableHeader>
 | 
				
			||||||
 | 
					            <TableBody>
 | 
				
			||||||
 | 
					              {filteredProperties.map((property) => (
 | 
				
			||||||
 | 
					                <TableRow key={property.id}>
 | 
				
			||||||
 | 
					                  <TableCell>{property.name}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{property.address}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{property.units}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Badge variant={property.occupancy === property.units ? 'default' : 'secondary'}>
 | 
				
			||||||
 | 
					                      {property.occupancy}/{property.units}
 | 
				
			||||||
 | 
					                    </Badge>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{property.rentRange}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <div className="flex space-x-2">
 | 
				
			||||||
 | 
					                      <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                        <Edit className="h-4 w-4" />
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                      <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                        <Trash2 className="h-4 w-4" />
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					            </TableBody>
 | 
				
			||||||
 | 
					          </Table>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Property Statistics</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <Tabs defaultValue="overview">
 | 
				
			||||||
 | 
					            <TabsList>
 | 
				
			||||||
 | 
					              <TabsTrigger value="overview">Overview</TabsTrigger>
 | 
				
			||||||
 | 
					              <TabsTrigger value="financial">Financial</TabsTrigger>
 | 
				
			||||||
 | 
					              <TabsTrigger value="maintenance">Maintenance</TabsTrigger>
 | 
				
			||||||
 | 
					            </TabsList>
 | 
				
			||||||
 | 
					            <TabsContent value="overview">
 | 
				
			||||||
 | 
					              <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Total Properties
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <Home className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">{properties.length}</div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Total Units
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <Users className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">
 | 
				
			||||||
 | 
					                      {properties.reduce((sum, p) => sum + p.units, 0)}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Occupancy Rate
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <Users className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">
 | 
				
			||||||
 | 
					                      {Math.round(
 | 
				
			||||||
 | 
					                        (properties.reduce((sum, p) => sum + p.occupancy, 0) /
 | 
				
			||||||
 | 
					                        properties.reduce((sum, p) => sum + p.units, 0)) * 100
 | 
				
			||||||
 | 
					                      )}%
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Avg. Rent
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <DollarSign className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">$1,450</div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					            <TabsContent value="financial">
 | 
				
			||||||
 | 
					              <p>Detailed financial information and statistics will be displayed here.</p>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					            <TabsContent value="maintenance">
 | 
				
			||||||
 | 
					              <p>Property maintenance history and upcoming tasks will be displayed here.</p>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					          </Tabs>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										236
									
								
								src/app/admin/tenants/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								src/app/admin/tenants/page.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,236 @@
 | 
				
			|||||||
 | 
					'use client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useState } from 'react'
 | 
				
			||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Input } from "~/components/ui/input"
 | 
				
			||||||
 | 
					import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"
 | 
				
			||||||
 | 
					import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"
 | 
				
			||||||
 | 
					import { Label } from "~/components/ui/label"
 | 
				
			||||||
 | 
					import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"
 | 
				
			||||||
 | 
					import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"
 | 
				
			||||||
 | 
					import { Badge } from "~/components/ui/badge"
 | 
				
			||||||
 | 
					import { Search, Plus, Edit, Trash2, Mail, Phone } from 'lucide-react'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for tenants
 | 
				
			||||||
 | 
					const tenants = [
 | 
				
			||||||
 | 
					  { id: 1, name: "John Doe", email: "john@example.com", phone: "123-456-7890", property: "123 Main St, Apt 4B", leaseEnd: "2023-12-31", status: "Active" },
 | 
				
			||||||
 | 
					  { id: 2, name: "Jane Smith", email: "jane@example.com", phone: "098-765-4321", property: "456 Elm St, Apt 2A", leaseEnd: "2024-03-15", status: "Active" },
 | 
				
			||||||
 | 
					  { id: 3, name: "Bob Johnson", email: "bob@example.com", phone: "555-123-4567", property: "789 Oak Rd, Apt 3C", leaseEnd: "2023-09-30", status: "Notice Given" },
 | 
				
			||||||
 | 
					  // Add more mock data as needed
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function TenantsPage() {
 | 
				
			||||||
 | 
					  const [searchTerm, setSearchTerm] = useState("")
 | 
				
			||||||
 | 
					  const [selectedStatus, setSelectedStatus] = useState("All")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const filteredTenants = tenants.filter(tenant => 
 | 
				
			||||||
 | 
					    (tenant.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
				
			||||||
 | 
					     tenant.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
				
			||||||
 | 
					     tenant.property.toLowerCase().includes(searchTerm.toLowerCase())) &&
 | 
				
			||||||
 | 
					    (selectedStatus === "All" || tenant.status === selectedStatus)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					            <CardTitle className="text-2xl">Tenants</CardTitle>
 | 
				
			||||||
 | 
					            <Dialog>
 | 
				
			||||||
 | 
					              <DialogTrigger asChild>
 | 
				
			||||||
 | 
					                <Button>
 | 
				
			||||||
 | 
					                  <Plus className="mr-2 h-4 w-4" /> Add Tenant
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </DialogTrigger>
 | 
				
			||||||
 | 
					              <DialogContent>
 | 
				
			||||||
 | 
					                <DialogHeader>
 | 
				
			||||||
 | 
					                  <DialogTitle>Add New Tenant</DialogTitle>
 | 
				
			||||||
 | 
					                  <DialogDescription>
 | 
				
			||||||
 | 
					                    Enter the details of the new tenant. Click save when you're done.
 | 
				
			||||||
 | 
					                  </DialogDescription>
 | 
				
			||||||
 | 
					                </DialogHeader>
 | 
				
			||||||
 | 
					                <div className="grid gap-4 py-4">
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="name" className="text-right">
 | 
				
			||||||
 | 
					                      Name
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="name" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="email" className="text-right">
 | 
				
			||||||
 | 
					                      Email
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="email" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="phone" className="text-right">
 | 
				
			||||||
 | 
					                      Phone
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="phone" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="property" className="text-right">
 | 
				
			||||||
 | 
					                      Property
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Select>
 | 
				
			||||||
 | 
					                      <SelectTrigger className="col-span-3">
 | 
				
			||||||
 | 
					                        <SelectValue placeholder="Select property" />
 | 
				
			||||||
 | 
					                      </SelectTrigger>
 | 
				
			||||||
 | 
					                      <SelectContent>
 | 
				
			||||||
 | 
					                        <SelectItem value="123 Main St, Apt 4B">123 Main St, Apt 4B</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="456 Elm St, Apt 2A">456 Elm St, Apt 2A</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="789 Oak Rd, Apt 3C">789 Oak Rd, Apt 3C</SelectItem>
 | 
				
			||||||
 | 
					                      </SelectContent>
 | 
				
			||||||
 | 
					                    </Select>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="leaseEnd" className="text-right">
 | 
				
			||||||
 | 
					                      Lease End Date
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="leaseEnd" type="date" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <DialogFooter>
 | 
				
			||||||
 | 
					                  <Button type="submit">Save Tenant</Button>
 | 
				
			||||||
 | 
					                </DialogFooter>
 | 
				
			||||||
 | 
					              </DialogContent>
 | 
				
			||||||
 | 
					            </Dialog>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center mb-4">
 | 
				
			||||||
 | 
					            <div className="flex items-center space-x-2">
 | 
				
			||||||
 | 
					              <Input
 | 
				
			||||||
 | 
					                placeholder="Search tenants..."
 | 
				
			||||||
 | 
					                value={searchTerm}
 | 
				
			||||||
 | 
					                onChange={(e) => setSearchTerm(e.target.value)}
 | 
				
			||||||
 | 
					                className="w-[300px]"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					              <Search className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <Select value={selectedStatus} onValueChange={setSelectedStatus}>
 | 
				
			||||||
 | 
					              <SelectTrigger className="w-[180px]">
 | 
				
			||||||
 | 
					                <SelectValue placeholder="Filter by status" />
 | 
				
			||||||
 | 
					              </SelectTrigger>
 | 
				
			||||||
 | 
					              <SelectContent>
 | 
				
			||||||
 | 
					                <SelectItem value="All">All Statuses</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Active">Active</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Notice Given">Notice Given</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Inactive">Inactive</SelectItem>
 | 
				
			||||||
 | 
					              </SelectContent>
 | 
				
			||||||
 | 
					            </Select>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <Table>
 | 
				
			||||||
 | 
					            <TableHeader>
 | 
				
			||||||
 | 
					              <TableRow>
 | 
				
			||||||
 | 
					                <TableHead>Name</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Property</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Lease End</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Status</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Actions</TableHead>
 | 
				
			||||||
 | 
					              </TableRow>
 | 
				
			||||||
 | 
					            </TableHeader>
 | 
				
			||||||
 | 
					            <TableBody>
 | 
				
			||||||
 | 
					              {filteredTenants.map((tenant) => (
 | 
				
			||||||
 | 
					                <TableRow key={tenant.id}>
 | 
				
			||||||
 | 
					                  <TableCell>{tenant.name}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{tenant.property}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{tenant.leaseEnd}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Badge variant={tenant.status === 'Active' ? 'default' : 'secondary'}>
 | 
				
			||||||
 | 
					                      {tenant.status}
 | 
				
			||||||
 | 
					                    </Badge>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <div className="flex space-x-2">
 | 
				
			||||||
 | 
					                      <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                        <Mail className="h-4 w-4" />
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                      <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                        <Phone className="h-4 w-4" />
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                      <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                        <Edit className="h-4 w-4" />
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                      <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                        <Trash2 className="h-4 w-4" />
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					            </TableBody>
 | 
				
			||||||
 | 
					          </Table>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Tenant Statistics</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <Tabs defaultValue="overview">
 | 
				
			||||||
 | 
					            <TabsList>
 | 
				
			||||||
 | 
					              <TabsTrigger value="overview">Overview</TabsTrigger>
 | 
				
			||||||
 | 
					              <TabsTrigger value="leases">Leases</TabsTrigger>
 | 
				
			||||||
 | 
					              <TabsTrigger value="payments">Payments</TabsTrigger>
 | 
				
			||||||
 | 
					            </TabsList>
 | 
				
			||||||
 | 
					            <TabsContent value="overview">
 | 
				
			||||||
 | 
					              <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Total Tenants
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">{tenants.length}</div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Active Leases
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">
 | 
				
			||||||
 | 
					                      {tenants.filter(t => t.status === 'Active').length}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Expiring Soon
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">2</div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Vacant Units
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">3</div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					            <TabsContent value="leases">
 | 
				
			||||||
 | 
					              <p>Detailed lease information and statistics will be displayed here.</p>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					            <TabsContent value="payments">
 | 
				
			||||||
 | 
					              <p>Tenant payment history and statistics will be displayed here.</p>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					          </Tabs>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										298
									
								
								src/app/admin/workorders/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								src/app/admin/workorders/page.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,298 @@
 | 
				
			|||||||
 | 
					'use client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useState } from 'react'
 | 
				
			||||||
 | 
					import { Button } from "~/components/ui/button"
 | 
				
			||||||
 | 
					import { Input } from "~/components/ui/input"
 | 
				
			||||||
 | 
					import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
 | 
				
			||||||
 | 
					import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"
 | 
				
			||||||
 | 
					import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"
 | 
				
			||||||
 | 
					import { Label } from "~/components/ui/label"
 | 
				
			||||||
 | 
					import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"
 | 
				
			||||||
 | 
					import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"
 | 
				
			||||||
 | 
					import { Badge } from "~/components/ui/badge"
 | 
				
			||||||
 | 
					import { Textarea } from "~/components/ui/textarea"
 | 
				
			||||||
 | 
					import { Search, Plus, Wrench, Clock, CheckCircle, AlertTriangle } from 'lucide-react'
 | 
				
			||||||
 | 
					import { ChartContainer, ChartTooltip, ChartTooltipContent } from "~/components/ui/chart"
 | 
				
			||||||
 | 
					import { Pie, PieChart, ResponsiveContainer, Cell } from "recharts"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for work orders
 | 
				
			||||||
 | 
					const workOrders = [
 | 
				
			||||||
 | 
					  { id: 1, property: "Sunset Apartments, Apt 4B", issue: "Leaky faucet", tenant: "John Doe", date: "2023-07-01", status: "Open" },
 | 
				
			||||||
 | 
					  { id: 2, property: "Oakwood Residences, Apt 2A", issue: "Broken AC", tenant: "Jane Smith", date: "2023-07-02", status: "In Progress" },
 | 
				
			||||||
 | 
					  { id: 3, property: "Riverside Condos, Apt 3C", issue: "Clogged drain", tenant: "Bob Johnson", date: "2023-07-03", status: "Completed" },
 | 
				
			||||||
 | 
					  { id: 4, property: "Sunset Apartments, Apt 2C", issue: "Electrical issue", tenant: "Alice Brown", date: "2023-07-04", status: "Open" },
 | 
				
			||||||
 | 
					  // Add more mock data as needed
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock data for work order status chart
 | 
				
			||||||
 | 
					const statusChartData = [
 | 
				
			||||||
 | 
					  { name: "Open", value: 5 },
 | 
				
			||||||
 | 
					  { name: "In Progress", value: 3 },
 | 
				
			||||||
 | 
					  { name: "Completed", value: 8 },
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const COLORS = ['#0088FE', '#00C49F', '#FFBB28']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function WorkOrdersPage() {
 | 
				
			||||||
 | 
					  const [searchTerm, setSearchTerm] = useState("")
 | 
				
			||||||
 | 
					  const [selectedStatus, setSelectedStatus] = useState("All")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const filteredWorkOrders = workOrders.filter(order => 
 | 
				
			||||||
 | 
					    (order.property.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
				
			||||||
 | 
					     order.issue.toLowerCase().includes(searchTerm.toLowerCase()) ||
 | 
				
			||||||
 | 
					     order.tenant.toLowerCase().includes(searchTerm.toLowerCase())) &&
 | 
				
			||||||
 | 
					    (selectedStatus === "All" || order.status === selectedStatus)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-8">
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center">
 | 
				
			||||||
 | 
					            <CardTitle className="text-2xl">Work Orders</CardTitle>
 | 
				
			||||||
 | 
					            <Dialog>
 | 
				
			||||||
 | 
					              <DialogTrigger asChild>
 | 
				
			||||||
 | 
					                <Button>
 | 
				
			||||||
 | 
					                  <Plus className="mr-2 h-4 w-4" /> Create Work Order
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </DialogTrigger>
 | 
				
			||||||
 | 
					              <DialogContent>
 | 
				
			||||||
 | 
					                <DialogHeader>
 | 
				
			||||||
 | 
					                  <DialogTitle>Create New Work Order</DialogTitle>
 | 
				
			||||||
 | 
					                  <DialogDescription>
 | 
				
			||||||
 | 
					                    Enter the details of the new work order. Click save when you're done.
 | 
				
			||||||
 | 
					                  </DialogDescription>
 | 
				
			||||||
 | 
					                </DialogHeader>
 | 
				
			||||||
 | 
					                <div className="grid gap-4 py-4">
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="property" className="text-right">
 | 
				
			||||||
 | 
					                      Property
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Select>
 | 
				
			||||||
 | 
					                      <SelectTrigger className="col-span-3">
 | 
				
			||||||
 | 
					                        <SelectValue placeholder="Select property" />
 | 
				
			||||||
 | 
					                      </SelectTrigger>
 | 
				
			||||||
 | 
					                      <SelectContent>
 | 
				
			||||||
 | 
					                        <SelectItem value="sunset-apartments">Sunset Apartments</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="oakwood-residences">Oakwood Residences</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="riverside-condos">Riverside Condos</SelectItem>
 | 
				
			||||||
 | 
					                      </SelectContent>
 | 
				
			||||||
 | 
					                    </Select>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="tenant" className="text-right">
 | 
				
			||||||
 | 
					                      Tenant
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Select>
 | 
				
			||||||
 | 
					                      <SelectTrigger className="col-span-3">
 | 
				
			||||||
 | 
					                        <SelectValue placeholder="Select tenant" />
 | 
				
			||||||
 | 
					                      </SelectTrigger>
 | 
				
			||||||
 | 
					                      <SelectContent>
 | 
				
			||||||
 | 
					                        <SelectItem value="john-doe">John Doe</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="jane-smith">Jane Smith</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="bob-johnson">Bob Johnson</SelectItem>
 | 
				
			||||||
 | 
					                      </SelectContent>
 | 
				
			||||||
 | 
					                    </Select>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="issue" className="text-right">
 | 
				
			||||||
 | 
					                      Issue
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Input id="issue" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="description" className="text-right">
 | 
				
			||||||
 | 
					                      Description
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Textarea id="description" className="col-span-3" />
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                  <div className="grid grid-cols-4 items-center gap-4">
 | 
				
			||||||
 | 
					                    <Label htmlFor="priority" className="text-right">
 | 
				
			||||||
 | 
					                      Priority
 | 
				
			||||||
 | 
					                    </Label>
 | 
				
			||||||
 | 
					                    <Select>
 | 
				
			||||||
 | 
					                      <SelectTrigger className="col-span-3">
 | 
				
			||||||
 | 
					                        <SelectValue placeholder="Select priority" />
 | 
				
			||||||
 | 
					                      </SelectTrigger>
 | 
				
			||||||
 | 
					                      <SelectContent>
 | 
				
			||||||
 | 
					                        <SelectItem value="low">Low</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="medium">Medium</SelectItem>
 | 
				
			||||||
 | 
					                        <SelectItem value="high">High</SelectItem>
 | 
				
			||||||
 | 
					                      </SelectContent>
 | 
				
			||||||
 | 
					                    </Select>
 | 
				
			||||||
 | 
					                  </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <DialogFooter>
 | 
				
			||||||
 | 
					                  <Button type="submit">Create Work Order</Button>
 | 
				
			||||||
 | 
					                </DialogFooter>
 | 
				
			||||||
 | 
					              </DialogContent>
 | 
				
			||||||
 | 
					            </Dialog>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="flex justify-between items-center mb-4">
 | 
				
			||||||
 | 
					            <div className="flex items-center space-x-2">
 | 
				
			||||||
 | 
					              <Input
 | 
				
			||||||
 | 
					                placeholder="Search work orders..."
 | 
				
			||||||
 | 
					                value={searchTerm}
 | 
				
			||||||
 | 
					                onChange={(e) => setSearchTerm(e.target.value)}
 | 
				
			||||||
 | 
					                className="w-[300px]"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					              <Search className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <Select value={selectedStatus} onValueChange={setSelectedStatus}>
 | 
				
			||||||
 | 
					              <SelectTrigger className="w-[180px]">
 | 
				
			||||||
 | 
					                <SelectValue placeholder="Filter by status" />
 | 
				
			||||||
 | 
					              </SelectTrigger>
 | 
				
			||||||
 | 
					              <SelectContent>
 | 
				
			||||||
 | 
					                <SelectItem value="All">All Statuses</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Open">Open</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="In Progress">In Progress</SelectItem>
 | 
				
			||||||
 | 
					                <SelectItem value="Completed">Completed</SelectItem>
 | 
				
			||||||
 | 
					              </SelectContent>
 | 
				
			||||||
 | 
					            </Select>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <Table>
 | 
				
			||||||
 | 
					            <TableHeader>
 | 
				
			||||||
 | 
					              <TableRow>
 | 
				
			||||||
 | 
					                <TableHead>Property</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Issue</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Tenant</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Date</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Status</TableHead>
 | 
				
			||||||
 | 
					                <TableHead>Actions</TableHead>
 | 
				
			||||||
 | 
					              </TableRow>
 | 
				
			||||||
 | 
					            </TableHeader>
 | 
				
			||||||
 | 
					            <TableBody>
 | 
				
			||||||
 | 
					              {filteredWorkOrders.map((order) => (
 | 
				
			||||||
 | 
					                <TableRow key={order.id}>
 | 
				
			||||||
 | 
					                  <TableCell>{order.property}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{order.issue}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{order.tenant}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{order.date}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Badge variant={
 | 
				
			||||||
 | 
					                      order.status === 'Open' ? 'default' :
 | 
				
			||||||
 | 
					                      order.status === 'In Progress' ? 'secondary' :
 | 
				
			||||||
 | 
					                      'success'
 | 
				
			||||||
 | 
					                    }>
 | 
				
			||||||
 | 
					                      {order.status}
 | 
				
			||||||
 | 
					                    </Badge>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <Button variant="ghost" size="icon">
 | 
				
			||||||
 | 
					                      <Wrench className="h-4 w-4" />
 | 
				
			||||||
 | 
					                    </Button>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					            </TableBody>
 | 
				
			||||||
 | 
					          </Table>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Card>
 | 
				
			||||||
 | 
					        <CardHeader>
 | 
				
			||||||
 | 
					          <CardTitle>Work Order Statistics</CardTitle>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <Tabs defaultValue="overview">
 | 
				
			||||||
 | 
					            <TabsList>
 | 
				
			||||||
 | 
					              <TabsTrigger value="overview">Overview</TabsTrigger>
 | 
				
			||||||
 | 
					              <TabsTrigger value="trends">Trends</TabsTrigger>
 | 
				
			||||||
 | 
					            </TabsList>
 | 
				
			||||||
 | 
					            <TabsContent value="overview">
 | 
				
			||||||
 | 
					              <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Total Work Orders
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <Wrench className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">{workOrders.length}</div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Open Work Orders
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <AlertTriangle className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">
 | 
				
			||||||
 | 
					                      {workOrders.filter(order => order.status === 'Open').length}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      In Progress
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <Clock className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">
 | 
				
			||||||
 | 
					                      {workOrders.filter(order => order.status === 'In Progress').length}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					                <Card>
 | 
				
			||||||
 | 
					                  <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
 | 
				
			||||||
 | 
					                    <CardTitle className="text-sm font-medium">
 | 
				
			||||||
 | 
					                      Completed
 | 
				
			||||||
 | 
					                    </CardTitle>
 | 
				
			||||||
 | 
					                    <CheckCircle className="h-4 w-4 text-muted-foreground" />
 | 
				
			||||||
 | 
					                  </CardHeader>
 | 
				
			||||||
 | 
					                  <CardContent>
 | 
				
			||||||
 | 
					                    <div className="text-2xl font-bold">
 | 
				
			||||||
 | 
					                      {workOrders.filter(order => order.status === 'Completed').length}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </CardContent>
 | 
				
			||||||
 | 
					                </Card>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					            <TabsContent value="trends">
 | 
				
			||||||
 | 
					              <Card>
 | 
				
			||||||
 | 
					                <CardHeader>
 | 
				
			||||||
 | 
					                  <CardTitle>Work Order Status Distribution</CardTitle>
 | 
				
			||||||
 | 
					                </CardHeader>
 | 
				
			||||||
 | 
					                <CardContent>
 | 
				
			||||||
 | 
					                  <ChartContainer config={{
 | 
				
			||||||
 | 
					                    status: {
 | 
				
			||||||
 | 
					                      label: "Status",
 | 
				
			||||||
 | 
					                      color: "hsl(var(--chart-1))",
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  }} className="h-[300px]">
 | 
				
			||||||
 | 
					                    <ResponsiveContainer width="100%" height="100%">
 | 
				
			||||||
 | 
					                      <PieChart>
 | 
				
			||||||
 | 
					                        <Pie
 | 
				
			||||||
 | 
					                          data={statusChartData}
 | 
				
			||||||
 | 
					                          cx="50%"
 | 
				
			||||||
 | 
					                          cy="50%"
 | 
				
			||||||
 | 
					                          labelLine={false}
 | 
				
			||||||
 | 
					                          outerRadius={80}
 | 
				
			||||||
 | 
					                          fill="#8884d8"
 | 
				
			||||||
 | 
					                          dataKey="value"
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                          {statusChartData.map((entry, index) => (
 | 
				
			||||||
 | 
					                            <Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
 | 
				
			||||||
 | 
					                          ))}
 | 
				
			||||||
 | 
					                        </Pie>
 | 
				
			||||||
 | 
					                        <ChartTooltip content={<ChartTooltipContent />} />
 | 
				
			||||||
 | 
					                      </PieChart>
 | 
				
			||||||
 | 
					                    </ResponsiveContainer>
 | 
				
			||||||
 | 
					                  </ChartContainer>
 | 
				
			||||||
 | 
					                </CardContent>
 | 
				
			||||||
 | 
					              </Card>
 | 
				
			||||||
 | 
					            </TabsContent>
 | 
				
			||||||
 | 
					          </Tabs>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/components/ui/badge.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/components/ui/badge.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import * as React from "react"
 | 
				
			||||||
 | 
					import { cva, type VariantProps } from "class-variance-authority"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cn } from "~/lib/utils"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const badgeVariants = cva(
 | 
				
			||||||
 | 
					  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    variants: {
 | 
				
			||||||
 | 
					      variant: {
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
 | 
				
			||||||
 | 
					        secondary:
 | 
				
			||||||
 | 
					          "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
 | 
				
			||||||
 | 
					        destructive:
 | 
				
			||||||
 | 
					          "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
 | 
				
			||||||
 | 
					        outline: "text-foreground",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    defaultVariants: {
 | 
				
			||||||
 | 
					      variant: "default",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface BadgeProps
 | 
				
			||||||
 | 
					  extends React.HTMLAttributes<HTMLDivElement>,
 | 
				
			||||||
 | 
					    VariantProps<typeof badgeVariants> {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Badge({ className, variant, ...props }: BadgeProps) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className={cn(badgeVariants({ variant }), className)} {...props} />
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Badge, badgeVariants }
 | 
				
			||||||
							
								
								
									
										66
									
								
								src/components/ui/calendar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/components/ui/calendar.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					"use client"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as React from "react"
 | 
				
			||||||
 | 
					import { ChevronLeft, ChevronRight } from "lucide-react"
 | 
				
			||||||
 | 
					import { DayPicker } from "react-day-picker"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cn } from "~/lib/utils"
 | 
				
			||||||
 | 
					import { buttonVariants } from "~/components/ui/button"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CalendarProps = React.ComponentProps<typeof DayPicker>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Calendar({
 | 
				
			||||||
 | 
					  className,
 | 
				
			||||||
 | 
					  classNames,
 | 
				
			||||||
 | 
					  showOutsideDays = true,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: CalendarProps) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <DayPicker
 | 
				
			||||||
 | 
					      showOutsideDays={showOutsideDays}
 | 
				
			||||||
 | 
					      className={cn("p-3", className)}
 | 
				
			||||||
 | 
					      classNames={{
 | 
				
			||||||
 | 
					        months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
 | 
				
			||||||
 | 
					        month: "space-y-4",
 | 
				
			||||||
 | 
					        caption: "flex justify-center pt-1 relative items-center",
 | 
				
			||||||
 | 
					        caption_label: "text-sm font-medium",
 | 
				
			||||||
 | 
					        nav: "space-x-1 flex items-center",
 | 
				
			||||||
 | 
					        nav_button: cn(
 | 
				
			||||||
 | 
					          buttonVariants({ variant: "outline" }),
 | 
				
			||||||
 | 
					          "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        nav_button_previous: "absolute left-1",
 | 
				
			||||||
 | 
					        nav_button_next: "absolute right-1",
 | 
				
			||||||
 | 
					        table: "w-full border-collapse space-y-1",
 | 
				
			||||||
 | 
					        head_row: "flex",
 | 
				
			||||||
 | 
					        head_cell:
 | 
				
			||||||
 | 
					          "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
 | 
				
			||||||
 | 
					        row: "flex w-full mt-2",
 | 
				
			||||||
 | 
					        cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
 | 
				
			||||||
 | 
					        day: cn(
 | 
				
			||||||
 | 
					          buttonVariants({ variant: "ghost" }),
 | 
				
			||||||
 | 
					          "h-9 w-9 p-0 font-normal aria-selected:opacity-100"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        day_range_end: "day-range-end",
 | 
				
			||||||
 | 
					        day_selected:
 | 
				
			||||||
 | 
					          "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
 | 
				
			||||||
 | 
					        day_today: "bg-accent text-accent-foreground",
 | 
				
			||||||
 | 
					        day_outside:
 | 
				
			||||||
 | 
					          "day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
 | 
				
			||||||
 | 
					        day_disabled: "text-muted-foreground opacity-50",
 | 
				
			||||||
 | 
					        day_range_middle:
 | 
				
			||||||
 | 
					          "aria-selected:bg-accent aria-selected:text-accent-foreground",
 | 
				
			||||||
 | 
					        day_hidden: "invisible",
 | 
				
			||||||
 | 
					        ...classNames,
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      components={{
 | 
				
			||||||
 | 
					        IconLeft: ({ ...props }) => <ChevronLeft className="h-4 w-4" />,
 | 
				
			||||||
 | 
					        IconRight: ({ ...props }) => <ChevronRight className="h-4 w-4" />,
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					Calendar.displayName = "Calendar"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Calendar }
 | 
				
			||||||
							
								
								
									
										365
									
								
								src/components/ui/chart.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								src/components/ui/chart.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,365 @@
 | 
				
			|||||||
 | 
					"use client"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as React from "react"
 | 
				
			||||||
 | 
					import * as RechartsPrimitive from "recharts"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cn } from "~/lib/utils"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Format: { THEME_NAME: CSS_SELECTOR }
 | 
				
			||||||
 | 
					const THEMES = { light: "", dark: ".dark" } as const
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ChartConfig = {
 | 
				
			||||||
 | 
					  [k in string]: {
 | 
				
			||||||
 | 
					    label?: React.ReactNode
 | 
				
			||||||
 | 
					    icon?: React.ComponentType
 | 
				
			||||||
 | 
					  } & (
 | 
				
			||||||
 | 
					    | { color?: string; theme?: never }
 | 
				
			||||||
 | 
					    | { color?: never; theme: Record<keyof typeof THEMES, string> }
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ChartContextProps = {
 | 
				
			||||||
 | 
					  config: ChartConfig
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChartContext = React.createContext<ChartContextProps | null>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function useChart() {
 | 
				
			||||||
 | 
					  const context = React.useContext(ChartContext)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!context) {
 | 
				
			||||||
 | 
					    throw new Error("useChart must be used within a <ChartContainer />")
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return context
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChartContainer = React.forwardRef<
 | 
				
			||||||
 | 
					  HTMLDivElement,
 | 
				
			||||||
 | 
					  React.ComponentProps<"div"> & {
 | 
				
			||||||
 | 
					    config: ChartConfig
 | 
				
			||||||
 | 
					    children: React.ComponentProps<
 | 
				
			||||||
 | 
					      typeof RechartsPrimitive.ResponsiveContainer
 | 
				
			||||||
 | 
					    >["children"]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					>(({ id, className, children, config, ...props }, ref) => {
 | 
				
			||||||
 | 
					  const uniqueId = React.useId()
 | 
				
			||||||
 | 
					  const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <ChartContext.Provider value={{ config }}>
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        data-chart={chartId}
 | 
				
			||||||
 | 
					        ref={ref}
 | 
				
			||||||
 | 
					        className={cn(
 | 
				
			||||||
 | 
					          "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
 | 
				
			||||||
 | 
					          className
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <ChartStyle id={chartId} config={config} />
 | 
				
			||||||
 | 
					        <RechartsPrimitive.ResponsiveContainer>
 | 
				
			||||||
 | 
					          {children}
 | 
				
			||||||
 | 
					        </RechartsPrimitive.ResponsiveContainer>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </ChartContext.Provider>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					ChartContainer.displayName = "Chart"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
 | 
				
			||||||
 | 
					  const colorConfig = Object.entries(config).filter(
 | 
				
			||||||
 | 
					    ([_, config]) => config.theme || config.color
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!colorConfig.length) {
 | 
				
			||||||
 | 
					    return null
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <style
 | 
				
			||||||
 | 
					      dangerouslySetInnerHTML={{
 | 
				
			||||||
 | 
					        __html: Object.entries(THEMES)
 | 
				
			||||||
 | 
					          .map(
 | 
				
			||||||
 | 
					            ([theme, prefix]) => `
 | 
				
			||||||
 | 
					${prefix} [data-chart=${id}] {
 | 
				
			||||||
 | 
					${colorConfig
 | 
				
			||||||
 | 
					  .map(([key, itemConfig]) => {
 | 
				
			||||||
 | 
					    const color =
 | 
				
			||||||
 | 
					      itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
 | 
				
			||||||
 | 
					      itemConfig.color
 | 
				
			||||||
 | 
					    return color ? `  --color-${key}: ${color};` : null
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .join("\n")}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          .join("\n"),
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChartTooltip = RechartsPrimitive.Tooltip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChartTooltipContent = React.forwardRef<
 | 
				
			||||||
 | 
					  HTMLDivElement,
 | 
				
			||||||
 | 
					  React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
 | 
				
			||||||
 | 
					    React.ComponentProps<"div"> & {
 | 
				
			||||||
 | 
					      hideLabel?: boolean
 | 
				
			||||||
 | 
					      hideIndicator?: boolean
 | 
				
			||||||
 | 
					      indicator?: "line" | "dot" | "dashed"
 | 
				
			||||||
 | 
					      nameKey?: string
 | 
				
			||||||
 | 
					      labelKey?: string
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					>(
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      active,
 | 
				
			||||||
 | 
					      payload,
 | 
				
			||||||
 | 
					      className,
 | 
				
			||||||
 | 
					      indicator = "dot",
 | 
				
			||||||
 | 
					      hideLabel = false,
 | 
				
			||||||
 | 
					      hideIndicator = false,
 | 
				
			||||||
 | 
					      label,
 | 
				
			||||||
 | 
					      labelFormatter,
 | 
				
			||||||
 | 
					      labelClassName,
 | 
				
			||||||
 | 
					      formatter,
 | 
				
			||||||
 | 
					      color,
 | 
				
			||||||
 | 
					      nameKey,
 | 
				
			||||||
 | 
					      labelKey,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    ref
 | 
				
			||||||
 | 
					  ) => {
 | 
				
			||||||
 | 
					    const { config } = useChart()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const tooltipLabel = React.useMemo(() => {
 | 
				
			||||||
 | 
					      if (hideLabel || !payload?.length) {
 | 
				
			||||||
 | 
					        return null
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const [item] = payload
 | 
				
			||||||
 | 
					      const key = `${labelKey || item.dataKey || item.name || "value"}`
 | 
				
			||||||
 | 
					      const itemConfig = getPayloadConfigFromPayload(config, item, key)
 | 
				
			||||||
 | 
					      const value =
 | 
				
			||||||
 | 
					        !labelKey && typeof label === "string"
 | 
				
			||||||
 | 
					          ? config[label as keyof typeof config]?.label || label
 | 
				
			||||||
 | 
					          : itemConfig?.label
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (labelFormatter) {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					          <div className={cn("font-medium", labelClassName)}>
 | 
				
			||||||
 | 
					            {labelFormatter(value, payload)}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!value) {
 | 
				
			||||||
 | 
					        return null
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return <div className={cn("font-medium", labelClassName)}>{value}</div>
 | 
				
			||||||
 | 
					    }, [
 | 
				
			||||||
 | 
					      label,
 | 
				
			||||||
 | 
					      labelFormatter,
 | 
				
			||||||
 | 
					      payload,
 | 
				
			||||||
 | 
					      hideLabel,
 | 
				
			||||||
 | 
					      labelClassName,
 | 
				
			||||||
 | 
					      config,
 | 
				
			||||||
 | 
					      labelKey,
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!active || !payload?.length) {
 | 
				
			||||||
 | 
					      return null
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const nestLabel = payload.length === 1 && indicator !== "dot"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        ref={ref}
 | 
				
			||||||
 | 
					        className={cn(
 | 
				
			||||||
 | 
					          "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
 | 
				
			||||||
 | 
					          className
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {!nestLabel ? tooltipLabel : null}
 | 
				
			||||||
 | 
					        <div className="grid gap-1.5">
 | 
				
			||||||
 | 
					          {payload.map((item, index) => {
 | 
				
			||||||
 | 
					            const key = `${nameKey || item.name || item.dataKey || "value"}`
 | 
				
			||||||
 | 
					            const itemConfig = getPayloadConfigFromPayload(config, item, key)
 | 
				
			||||||
 | 
					            const indicatorColor = color || item.payload.fill || item.color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
 | 
					              <div
 | 
				
			||||||
 | 
					                key={item.dataKey}
 | 
				
			||||||
 | 
					                className={cn(
 | 
				
			||||||
 | 
					                  "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
 | 
				
			||||||
 | 
					                  indicator === "dot" && "items-center"
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                {formatter && item?.value !== undefined && item.name ? (
 | 
				
			||||||
 | 
					                  formatter(item.value, item.name, item, index, item.payload)
 | 
				
			||||||
 | 
					                ) : (
 | 
				
			||||||
 | 
					                  <>
 | 
				
			||||||
 | 
					                    {itemConfig?.icon ? (
 | 
				
			||||||
 | 
					                      <itemConfig.icon />
 | 
				
			||||||
 | 
					                    ) : (
 | 
				
			||||||
 | 
					                      !hideIndicator && (
 | 
				
			||||||
 | 
					                        <div
 | 
				
			||||||
 | 
					                          className={cn(
 | 
				
			||||||
 | 
					                            "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                              "h-2.5 w-2.5": indicator === "dot",
 | 
				
			||||||
 | 
					                              "w-1": indicator === "line",
 | 
				
			||||||
 | 
					                              "w-0 border-[1.5px] border-dashed bg-transparent":
 | 
				
			||||||
 | 
					                                indicator === "dashed",
 | 
				
			||||||
 | 
					                              "my-0.5": nestLabel && indicator === "dashed",
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                          )}
 | 
				
			||||||
 | 
					                          style={
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                              "--color-bg": indicatorColor,
 | 
				
			||||||
 | 
					                              "--color-border": indicatorColor,
 | 
				
			||||||
 | 
					                            } as React.CSSProperties
 | 
				
			||||||
 | 
					                          }
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                      )
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                    <div
 | 
				
			||||||
 | 
					                      className={cn(
 | 
				
			||||||
 | 
					                        "flex flex-1 justify-between leading-none",
 | 
				
			||||||
 | 
					                        nestLabel ? "items-end" : "items-center"
 | 
				
			||||||
 | 
					                      )}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                      <div className="grid gap-1.5">
 | 
				
			||||||
 | 
					                        {nestLabel ? tooltipLabel : null}
 | 
				
			||||||
 | 
					                        <span className="text-muted-foreground">
 | 
				
			||||||
 | 
					                          {itemConfig?.label || item.name}
 | 
				
			||||||
 | 
					                        </span>
 | 
				
			||||||
 | 
					                      </div>
 | 
				
			||||||
 | 
					                      {item.value && (
 | 
				
			||||||
 | 
					                        <span className="font-mono font-medium tabular-nums text-foreground">
 | 
				
			||||||
 | 
					                          {item.value.toLocaleString()}
 | 
				
			||||||
 | 
					                        </span>
 | 
				
			||||||
 | 
					                      )}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                  </>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          })}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					ChartTooltipContent.displayName = "ChartTooltip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChartLegend = RechartsPrimitive.Legend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ChartLegendContent = React.forwardRef<
 | 
				
			||||||
 | 
					  HTMLDivElement,
 | 
				
			||||||
 | 
					  React.ComponentProps<"div"> &
 | 
				
			||||||
 | 
					    Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
 | 
				
			||||||
 | 
					      hideIcon?: boolean
 | 
				
			||||||
 | 
					      nameKey?: string
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					>(
 | 
				
			||||||
 | 
					  (
 | 
				
			||||||
 | 
					    { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
 | 
				
			||||||
 | 
					    ref
 | 
				
			||||||
 | 
					  ) => {
 | 
				
			||||||
 | 
					    const { config } = useChart()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!payload?.length) {
 | 
				
			||||||
 | 
					      return null
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        ref={ref}
 | 
				
			||||||
 | 
					        className={cn(
 | 
				
			||||||
 | 
					          "flex items-center justify-center gap-4",
 | 
				
			||||||
 | 
					          verticalAlign === "top" ? "pb-3" : "pt-3",
 | 
				
			||||||
 | 
					          className
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {payload.map((item) => {
 | 
				
			||||||
 | 
					          const key = `${nameKey || item.dataKey || "value"}`
 | 
				
			||||||
 | 
					          const itemConfig = getPayloadConfigFromPayload(config, item, key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <div
 | 
				
			||||||
 | 
					              key={item.value}
 | 
				
			||||||
 | 
					              className={cn(
 | 
				
			||||||
 | 
					                "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              {itemConfig?.icon && !hideIcon ? (
 | 
				
			||||||
 | 
					                <itemConfig.icon />
 | 
				
			||||||
 | 
					              ) : (
 | 
				
			||||||
 | 
					                <div
 | 
				
			||||||
 | 
					                  className="h-2 w-2 shrink-0 rounded-[2px]"
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    backgroundColor: item.color,
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					              {itemConfig?.label}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        })}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					ChartLegendContent.displayName = "ChartLegend"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Helper to extract item config from a payload.
 | 
				
			||||||
 | 
					function getPayloadConfigFromPayload(
 | 
				
			||||||
 | 
					  config: ChartConfig,
 | 
				
			||||||
 | 
					  payload: unknown,
 | 
				
			||||||
 | 
					  key: string
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  if (typeof payload !== "object" || payload === null) {
 | 
				
			||||||
 | 
					    return undefined
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const payloadPayload =
 | 
				
			||||||
 | 
					    "payload" in payload &&
 | 
				
			||||||
 | 
					    typeof payload.payload === "object" &&
 | 
				
			||||||
 | 
					    payload.payload !== null
 | 
				
			||||||
 | 
					      ? payload.payload
 | 
				
			||||||
 | 
					      : undefined
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let configLabelKey: string = key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (
 | 
				
			||||||
 | 
					    key in payload &&
 | 
				
			||||||
 | 
					    typeof payload[key as keyof typeof payload] === "string"
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    configLabelKey = payload[key as keyof typeof payload] as string
 | 
				
			||||||
 | 
					  } else if (
 | 
				
			||||||
 | 
					    payloadPayload &&
 | 
				
			||||||
 | 
					    key in payloadPayload &&
 | 
				
			||||||
 | 
					    typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    configLabelKey = payloadPayload[
 | 
				
			||||||
 | 
					      key as keyof typeof payloadPayload
 | 
				
			||||||
 | 
					    ] as string
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return configLabelKey in config
 | 
				
			||||||
 | 
					    ? config[configLabelKey]
 | 
				
			||||||
 | 
					    : config[key as keyof typeof config]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  ChartContainer,
 | 
				
			||||||
 | 
					  ChartTooltip,
 | 
				
			||||||
 | 
					  ChartTooltipContent,
 | 
				
			||||||
 | 
					  ChartLegend,
 | 
				
			||||||
 | 
					  ChartLegendContent,
 | 
				
			||||||
 | 
					  ChartStyle,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user