Skip to content

Commit

Permalink
Merge branch 'main' into jinkang/ijp-22-eslint-unable-to-resolve-in-i…
Browse files Browse the repository at this point in the history
…mport-path
  • Loading branch information
jinkang-0 authored Oct 13, 2023
2 parents f52cd72 + 0983d34 commit 6c3fae0
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 53 deletions.
6 changes: 5 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}
const nextConfig = {
compiler: {
styledComponents: true
}
}

module.exports = nextConfig
3 changes: 3 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 7 additions & 9 deletions src/api/supabase/queries/cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@ import supabase from '../createClient';

export async function getAllCases() {
const { data, error } = await supabase.from('cases').select();
if (error) throw error;
return data;
}

if (error) {
throw new Error(`An error occurred trying to read cases: ${error}`);
}

export async function getNCases(n: number) {
const { data, error } = await supabase.from('cases').select().limit(n);
if (error) throw error;
return data;
}

export async function getCaseById(id: UUID) {
const { data, error } = await supabase.from('cases').select().eq('id', id);

if (error) {
throw new Error(`An error occurred trying to read cases: ${error}`);
}

if (error) throw error;
return data;
}
80 changes: 37 additions & 43 deletions src/app/cases/page.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,52 @@
'use client';

import { useEffect, useState } from 'react';
import { getAllCases } from '../../api/supabase/queries/cases';
import { UUID } from 'crypto';
import { CaseListing } from '../../types/schemaTypes';
import { getNCases } from '../../api/supabase/queries/cases';
import ListingCard from '../../components/ListingCard/ListingCard';
import {
CardColumn,
CaseDetailDisplay,
CaseDetails,
MainDisplay,
PageContainer,
} from './styles';
import { H1, H2 } from '../../styles/text';

export default function Page() {
const [data, setData] = useState<CaseListing[]>([]);
const [caseData, setCaseData] = useState<CaseListing[]>([]);
const [selectedCard, setSelectedCard] = useState<UUID>();

// react hooks
useEffect(() => {
getAllCases().then(casesData => {
setData(casesData);
getNCases(20).then(casesData => {
setCaseData(casesData);
});
}, []);

// page structure
return (
<div>
<table>
<thead>
<tr>
<th>id</th>
<th>summary</th>
<th>languages</th>
<th>country</th>
<th>legalServerId</th>
<th>clientInitials</th>
<th>timeToComplete</th>
<th>isRemote</th>
<th>clientLocation</th>
<th>program</th>
<th>upcomingHearingDate</th>
<th>needsInterpreter</th>
<th>interestIds</th>
</tr>
</thead>
<tbody>
{data.map(d => (
<tr key={d.id}>
<td>{d.id}</td>
<td>{d.summary}</td>
<td>{JSON.stringify(d.languages)}</td>
<td>{d.country}</td>
<td>{d.legal_server_id}</td>
<td>{d.client_initials}</td>
<td>{d.time_to_complete}</td>
<td>{d.is_remote}</td>
<td>{d.client_location}</td>
<td>{d.program}</td>
<td>{d.upcoming_hearing_date}</td>
<td>{JSON.stringify(d.needs_interpreter)}</td>
<td>{JSON.stringify(d.interest_ids)}</td>
</tr>
<PageContainer>
<H1>Browse Available Cases</H1>
<MainDisplay>
<CardColumn>
{caseData.map(c => (
<ListingCard
key={c.id}
caseData={c}
isSelected={c.id === selectedCard}
onClick={() => setSelectedCard(c.id)}
/>
))}
</tbody>
</table>
</div>
</CardColumn>
<CaseDetailDisplay>
{/* proof of concept -- to turn into component later */}
<CaseDetails>
<H2>Case details.</H2>
</CaseDetails>
</CaseDetailDisplay>
</MainDisplay>
</PageContainer>
);
}
44 changes: 44 additions & 0 deletions src/app/cases/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import styled from 'styled-components';

// styled components
export const PageContainer = styled.div`
display: grid;
place-items: center;
padding: 2rem;
`;

export const MainDisplay = styled.main`
display: grid;
grid-template-columns: 5fr 10fr;
margin-top: 2rem;
width: 100%;
`;

// cards
export const CardColumn = styled.div`
display: flex;
flex-direction: column;
gap: 2rem;
border-right: 1px solid black;
padding-top: 0.5rem;
padding-right: 2rem;
`;

// case detail
export const CaseDetailDisplay = styled.aside`
position: relative;
width: 100%;
`;

export const CaseDetails = styled.div`
position: sticky;
top: 4rem;
background: white;
width: 90%;
height: 80vh;
border-radius: 20px;
margin: 0 auto;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
padding: 2rem;
border: 1px solid lightgray;
`;
5 changes: 5 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
58 changes: 58 additions & 0 deletions src/components/ListingCard/ListingCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import { UUID } from 'crypto';
import { CaseListing } from '../../types/schemaTypes';
import timestampStringToDate from '../../utils/helpers';
import { CardBody, TagRow, CardTag } from './styles';
import { H2 } from '../../styles/text';
import COLORS from '../../styles/colors';

export default function ListingCard({
caseData,
isSelected = false,
onClick,
}: {
caseData: CaseListing;
isSelected?: boolean;
onClick?: (id: UUID) => void;
}) {
// setup
const rolesNeeded = ['Attorney'].concat(
caseData.needs_interpreter ? ['Interpreter'] : [],
);

// helper functions
const parseDate = (d: Date): string =>
`${d.getMonth() + 1}/${d.getDate()}/${d.getFullYear()}`;

return (
<CardBody
$selected={isSelected}
onClick={onClick ? () => onClick(caseData.id) : undefined}
>
{/* hard-coded for now */}
<H2>Case title.</H2>
<p>
<strong>Languages: </strong>
{caseData.languages.join(', ')}
</p>
<p>
<strong>Case Deadline: </strong>
{parseDate(timestampStringToDate(caseData.time_to_complete))}
</p>
<TagRow>
{rolesNeeded.map(r => (
<CardTag
key={r}
color={
r === 'Interpreter'
? COLORS.interpreterColor
: COLORS.attorneyColor
}
>
{r}
</CardTag>
))}
</TagRow>
</CardBody>
);
}
32 changes: 32 additions & 0 deletions src/components/ListingCard/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import styled from 'styled-components';

// the card itself
export const CardBody = styled.div<{ $selected?: boolean }>`
display: flex;
flex-direction: column;
border: 1px solid lightgray;
padding: 1rem;
border-radius: 10px;
transition: 150ms;
cursor: pointer;
gap: 1rem;
${({ $selected }) => $selected && `border-color: #097A62`};
&:hover {
box-shadow: 0 4px 4px 1px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
`;

export const TagRow = styled.div`
display: flex;
gap: 1rem;
`;

export const CardTag = styled.span<{ color: string }>`
border-radius: 100px;
font-size: 0.8rem;
padding: 0.2rem 0.5rem;
background: ${({ color }) => color};
`;
6 changes: 6 additions & 0 deletions src/styles/colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const COLORS = {
attorneyColor: '#85b4d2',
interpreterColor: '#8dd89f',
};

export default COLORS;
13 changes: 13 additions & 0 deletions src/styles/text.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import styled from 'styled-components';

export const H1 = styled.h1`
display: block;
font-size: 2rem;
margin: 0;
margin-right: auto;
`;

export const H2 = styled.h2`
font-size: 1.5rem;
margin: 0;
`;

0 comments on commit 6c3fae0

Please sign in to comment.