2 files added
16 files modified
| | |
| | | Switch, |
| | | Route, |
| | | } from "react-router-dom"; |
| | | // import ShelterRESTService from "./Services/ShelterRESTService"; |
| | | import SheltersView from "./Views/SheltersView"; |
| | | import NewsView from "./Views/NewsView"; |
| | | import ShelterFakeService from "./Services/ShelterFakeService"; |
| | |
| | | import { AnimalService } from "./Services/AnimalService"; |
| | | import { AdoptionService } from "./Services/AdoptionService"; |
| | | import { ShelterService } from "./Services/ShelterService"; |
| | | import AnimalDetailsView from "./Views/AnimalDetailsView"; |
| | | import AnimalDetails from "./Components/AnimalDetails"; |
| | | // import ShelterRESTService from "./Services/ShelterRESTService"; |
| | | import AnimalDetailsView from "./Views/AnimalDetailsView"; |
| | | import ShelterDetailsView from "./Views/ShelterDetailsView"; |
| | | |
| | | // Services to connect to backends |
| | | |
| | | // Backend SERVICES |
| | | let animalService: AnimalService; |
| | | let adoptionService: AdoptionService; |
| | | let shelterService: ShelterService; |
| | | let newsService: NewsService; |
| | | |
| | | // Fake services for frontend-isolated developemtn |
| | | // shelterService = new ShelterFakeService(); |
| | | // newsService = new NewsFakeService(); |
| | | // animalService = new AnimalFakeService(); |
| | | // adoptionService = new AdoptionFakeService(); |
| | | if (process.env.REACT_APP_ADOPTION_SERVICE_URL) { |
| | | adoptionService = new AdoptionRESTService(process.env.REACT_APP_ADOPTION_SERVICE_URL || ""); |
| | | } else { |
| | | console.log("Warning: No service url provided. Using AdoptionFakeService"); |
| | | adoptionService = new AdoptionFakeService(); |
| | | } |
| | | |
| | | // Uncomment to use Real services |
| | | adoptionService = new AdoptionRESTService(process.env.REACT_APP_ADOPTION_SERVICE_URL || ""); |
| | | animalService = new AnimalRESTService(process.env.REACT_APP_ANIMAL_SERVICE_URL || ""); |
| | | shelterService = new ShelterRESTService(process.env.REACT_APP_SHELTER_SERVICE_URL || ""); |
| | | if (process.env.REACT_APP_ANIMAL_SERVICE_URL) { |
| | | animalService = new AnimalRESTService(process.env.REACT_APP_ANIMAL_SERVICE_URL || ""); |
| | | } else { |
| | | console.log("Warning: No service url provided. Using AnimalFakeService"); |
| | | animalService = new AnimalFakeService(); |
| | | } |
| | | |
| | | if (process.env.REACT_APP_SHELTER_SERVICE_URL) { |
| | | shelterService = new ShelterRESTService(process.env.REACT_APP_SHELTER_SERVICE_URL || ""); |
| | | } else { |
| | | console.log("Warning: No service url provided. Using ShelterFakeService"); |
| | | shelterService = new ShelterFakeService(); |
| | | } |
| | | |
| | | if (process.env.REACT_APP_SHELTER_SERVICE_URL) { |
| | | shelterService = new ShelterRESTService(process.env.REACT_APP_SHELTER_SERVICE_URL || ""); |
| | | } else { |
| | | console.log("Warning: No service url provided. Using ShelterFakeService"); |
| | | shelterService = new ShelterFakeService(); |
| | | } |
| | | |
| | | // TODO: Create REST News service |
| | | newsService = new NewsFakeService(); |
| | | |
| | | |
| | | // MAIN APP |
| | | // The main React component that runs the whole webapp |
| | | export default class App extends Component { |
| | | render() { |
| | |
| | | <NewsView newsService={newsService} /> |
| | | </Route> |
| | | } |
| | | <Route path={`/animals/:animalId`} component={AnimalDetails}> |
| | | {/* <AnimalDetailsView |
| | | // animalId={this.pro} |
| | | <Route path={"/animals/:animalId"} render={ (props) => |
| | | <AnimalDetailsView {...props} |
| | | animalService={animalService} |
| | | adoptionService={adoptionService}/> */} |
| | | adoptionService={adoptionService} |
| | | /> } > |
| | | </Route> |
| | | <Route path={"/shelters/:shelterId"} render={ (props) => |
| | | <ShelterDetailsView {...props} |
| | | shelterService={shelterService} |
| | | adoptionService={adoptionService} |
| | | /> } > |
| | | </Route> |
| | | </Structure> |
| | | </Switch> |
| | |
| | | import React from "react"; |
| | | import { Link } from "react-router-dom"; |
| | | import { |
| | | Gallery, GalleryItem, Card, CardBody, CardHeader, CardActions, Button, Alert |
| | | Gallery, GalleryItem, Card, CardBody, CardHeader, CardActions, Button |
| | | } from "@patternfly/react-core"; |
| | | import { AnimalService } from "../Services/AnimalService"; |
| | | import { Animal } from "../Models/Animal"; |
| | | import { AdoptionService } from "../Services/AdoptionService"; |
| | | import { Residency } from "../Models/Residency"; |
| | | import AnimalDetailsView from "../Views/AnimalDetailsView"; |
| | | import { Link, useHistory } from "react-router-dom"; |
| | | |
| | | |
| | | type AnimalListProps = { |
| | | animalService: AnimalService, |
| | | adoptionService: AdoptionService |
| | | animals: Animal[] |
| | | } |
| | | |
| | | type AnimalListState = { |
| | | animals: Animal[], |
| | | showAdoptSucessAlert: boolean, |
| | | showAdoptErrorAlert: boolean |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Card list to show adoptable animals and apply for adoption |
| | | */ |
| | | export default class AdoptableAnimalList extends React.Component<AnimalListProps, AnimalListState> { |
| | | |
| | | constructor(props: AnimalListProps) { |
| | | super(props); |
| | | this.state = { |
| | | animals: [], |
| | | showAdoptErrorAlert: false, |
| | | showAdoptSucessAlert: false |
| | | }; |
| | | } |
| | | |
| | | public async componentDidMount() { |
| | | const animals = await this.props.animalService.getAllAdoptable(); |
| | | this.setState({ |
| | | animals |
| | | }); |
| | | } |
| | | |
| | | private async handleAdoptButtonClick(animal: Animal) { |
| | | try { |
| | | // TODO: Application form |
| | | const adoptionApplication = { |
| | | username: "todo", |
| | | residency: Residency.HOUSE, |
| | | animalId: animal.animalId, |
| | | squareFootageOfHome: 100, |
| | | occupation: "todo", |
| | | ownOtherAnimals: false, |
| | | kidsUnder16: true, |
| | | email: "todo@todo.com" |
| | | }; |
| | | await this.props.adoptionService.applyForAdoption(adoptionApplication); |
| | | this.setState({ showAdoptSucessAlert: true }); |
| | | } catch { |
| | | this.setState({ showAdoptErrorAlert: true }); |
| | | } |
| | | |
| | | setTimeout(() => { |
| | | this.setState({ |
| | | showAdoptErrorAlert: false, |
| | | showAdoptSucessAlert: false, |
| | | }); |
| | | }, 2000); |
| | | |
| | | } |
| | | export default class AdoptableAnimalList extends React.Component<AnimalListProps> { |
| | | |
| | | public render() { |
| | | return ( |
| | | <React.Fragment> |
| | | {this.renderAdoptSuccessAlert()} |
| | | {this.renderAdoptErrorAlert()} |
| | | <Gallery> |
| | | {this.state.animals.map(animal => this.renderAnimalCard(animal))} |
| | | {this.props.animals.map(animal => this.renderAnimalCard(animal))} |
| | | </Gallery> |
| | | </React.Fragment> |
| | | ); |
| | |
| | | <CardBody> |
| | | <img src={pictureSrc} alt={animal.animalName}></img> |
| | | <CardActions> |
| | | {/* <Button onClick={() => this.handleAdoptButtonClick(animal)}> |
| | | Adopt |
| | | </Button> */} |
| | | <Link to={`/animals/${animal.animalId}`}> |
| | | <Button> |
| | | Details |
| | |
| | | |
| | | } |
| | | |
| | | private renderAdoptErrorAlert(): React.ReactNode | null { |
| | | if (this.state.showAdoptErrorAlert) { |
| | | return <Alert variant="danger" title="The adoption application failed" />; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private renderAdoptSuccessAlert(): React.ReactNode | null { |
| | | if (this.state.showAdoptSucessAlert) { |
| | | return <Alert variant="success" title="Adoption application sent successfully" />; |
| | | } |
| | | return null; |
| | | } |
| | | } |
New file |
| | |
| | | import React, { FormEvent } from "react"; |
| | | import { |
| | | Form, |
| | | FormGroup, |
| | | TextInput, |
| | | ActionGroup, |
| | | Button, |
| | | ButtonType, |
| | | FormSelect, |
| | | FormSelectOption, |
| | | Checkbox, |
| | | Alert |
| | | } from "@patternfly/react-core"; |
| | | import { AdoptionService } from "../Services/AdoptionService"; |
| | | import { Residency } from "../Models/Residency"; |
| | | |
| | | |
| | | type AdoptionFormProps = { |
| | | adoptionService: AdoptionService, |
| | | animalId: string |
| | | }; |
| | | |
| | | type AdoptionFormState = { |
| | | username: string, |
| | | email: string, |
| | | residency: Residency, |
| | | squareFootageOfHome: number, |
| | | kidsUnder16: boolean, |
| | | occupation: string, |
| | | ownOtherAnimals: boolean, |
| | | showAdoptSucessAlert: boolean, |
| | | showAdoptErrorAlert: boolean |
| | | }; |
| | | |
| | | export default class AdoptionForm extends React.Component<AdoptionFormProps, AdoptionFormState> { |
| | | |
| | | constructor(props: AdoptionFormProps) { |
| | | super(props); |
| | | this.state = { |
| | | username: "", |
| | | email: "", |
| | | occupation: "", |
| | | residency: Residency.APARTMENT, |
| | | squareFootageOfHome: 0, |
| | | kidsUnder16: false, |
| | | ownOtherAnimals: false, |
| | | showAdoptErrorAlert: false, |
| | | showAdoptSucessAlert: false |
| | | }; |
| | | } |
| | | |
| | | private handleNameChange(username: string) { |
| | | this.setState({ username }); |
| | | } |
| | | |
| | | private handleEmailChange(email: string) { |
| | | this.setState({ email }); |
| | | } |
| | | |
| | | private handleOccupationChange(occupation: string) { |
| | | this.setState({ occupation }); |
| | | } |
| | | |
| | | private handleResidencyChange(residency: Residency) { |
| | | this.setState({ residency }); |
| | | } |
| | | |
| | | private handleSquareFootageChange(squareFootageOfHome: string) { |
| | | this.setState({ squareFootageOfHome: parseInt(squareFootageOfHome) }); |
| | | } |
| | | |
| | | private handleKidsUnder16Change(kidsUnder16: boolean) { |
| | | this.setState({ kidsUnder16 }); |
| | | } |
| | | |
| | | private handleOwnOtherAnimalsChange(ownOtherAnimals: boolean) { |
| | | this.setState({ ownOtherAnimals }); |
| | | } |
| | | |
| | | private async handleFormSubmit(event: FormEvent) { |
| | | const application = { |
| | | animalId: this.props.animalId, |
| | | ...this.state |
| | | }; |
| | | |
| | | event.preventDefault(); |
| | | |
| | | try { |
| | | await this.props.adoptionService.applyForAdoption(application); |
| | | this.setState({ showAdoptSucessAlert: true }); |
| | | } catch { |
| | | this.setState({ showAdoptErrorAlert: true }); |
| | | } |
| | | |
| | | setTimeout(() => { |
| | | this.setState({ |
| | | showAdoptErrorAlert: false, |
| | | showAdoptSucessAlert: false, |
| | | }); |
| | | }, 2000); |
| | | } |
| | | |
| | | public render() { |
| | | const { |
| | | username, |
| | | email, |
| | | occupation, |
| | | residency, |
| | | squareFootageOfHome, |
| | | kidsUnder16, |
| | | ownOtherAnimals |
| | | } = this.state; |
| | | |
| | | return ( |
| | | <Form onSubmit={this.handleFormSubmit.bind(this)}> |
| | | {this.renderAdoptSuccessAlert()} |
| | | {this.renderAdoptErrorAlert()} |
| | | <FormGroup |
| | | label="Name" |
| | | isRequired |
| | | fieldId="simple-form-username" |
| | | helperText="Please provide your name" |
| | | > |
| | | <TextInput |
| | | isRequired |
| | | type="text" |
| | | id="simple-form-username" |
| | | name="simple-form-username" |
| | | aria-describedby="simple-form-name-helper" |
| | | value={username} |
| | | onChange={this.handleNameChange.bind(this)} |
| | | /> |
| | | </FormGroup> |
| | | <FormGroup label="Email" isRequired fieldId="simple-form-email"> |
| | | <TextInput |
| | | isRequired |
| | | type="email" |
| | | id="simple-form-email" |
| | | name="simple-form-email" |
| | | value={email} |
| | | onChange={this.handleEmailChange.bind(this)} |
| | | /> |
| | | </FormGroup> |
| | | <FormGroup |
| | | label="Occupation" |
| | | isRequired |
| | | fieldId="simple-form-occupation" |
| | | helperText="Please provide your occupation" |
| | | > |
| | | <TextInput |
| | | isRequired |
| | | type="text" |
| | | id="simple-form-occupation" |
| | | name="simple-form-occupation" |
| | | aria-describedby="simple-form-name-helper" |
| | | value={occupation} |
| | | onChange={this.handleOccupationChange.bind(this)} |
| | | /> |
| | | </FormGroup> |
| | | <FormGroup |
| | | label="Residency" |
| | | isRequired |
| | | fieldId="simple-form-residency" |
| | | helperText="Please provide the address" |
| | | > |
| | | <FormSelect |
| | | value={residency} |
| | | onChange={(residency) => this.handleResidencyChange(residency as Residency)} |
| | | aria-label="Select Residency"> |
| | | <FormSelectOption |
| | | key={Residency.HOUSE} |
| | | value={Residency.HOUSE} |
| | | label={Residency.HOUSE} |
| | | /> |
| | | <FormSelectOption |
| | | key={Residency.APARTMENT} |
| | | value={Residency.APARTMENT} |
| | | label={Residency.APARTMENT} |
| | | /> |
| | | </FormSelect> |
| | | </FormGroup> |
| | | |
| | | <FormGroup label="Square Footage of Home" isRequired fieldId="simple-form-footage"> |
| | | <TextInput |
| | | isRequired |
| | | type="number" |
| | | id="simple-form-footage" |
| | | name="simple-form-footage" |
| | | value={squareFootageOfHome} |
| | | onChange={this.handleSquareFootageChange.bind(this)} |
| | | /> |
| | | </FormGroup> |
| | | |
| | | <FormGroup fieldId="simple-form-kids"> |
| | | <Checkbox |
| | | label="I have kids under 16" |
| | | id="simple-form-kids" |
| | | name="simple-form-kids" |
| | | aria-label="I have kids under 16" |
| | | isChecked={kidsUnder16} |
| | | onChange={this.handleKidsUnder16Change.bind(this)} /> |
| | | </FormGroup> |
| | | |
| | | <FormGroup fieldId="simple-form-other-animals"> |
| | | <Checkbox |
| | | label="I own other animals" |
| | | id="simple-form-other-animals" |
| | | name="simple-form-other-animals" |
| | | aria-label="I own other animals" |
| | | isChecked={ownOtherAnimals} |
| | | onChange={this.handleOwnOtherAnimalsChange.bind(this)} /> |
| | | </FormGroup> |
| | | |
| | | <ActionGroup> |
| | | <Button variant="primary" type={ButtonType.submit}>Apply for Adoption</Button> |
| | | </ActionGroup> |
| | | |
| | | </Form> |
| | | ); |
| | | } |
| | | |
| | | private renderAdoptErrorAlert(): React.ReactNode | null { |
| | | if (this.state.showAdoptErrorAlert) { |
| | | return <Alert variant="danger" title="The adoption application failed" />; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private renderAdoptSuccessAlert(): React.ReactNode | null { |
| | | if (this.state.showAdoptSucessAlert) { |
| | | return <Alert |
| | | variant="success" |
| | | title="Congratulations! The Adoption application was sent." |
| | | />; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | } |
| | | |
| | | |
| | |
| | | id="simple-form-email" |
| | | name="simple-form-email" |
| | | value={email} |
| | | onChange={this.handleEmailChange} |
| | | onChange={this.handleEmailChange.bind(this)} |
| | | /> |
| | | </FormGroup> |
| | | <FormGroup label="Phone number" isRequired fieldId="simple-form-number"> |
| | |
| | | placeholder="555-555-5555" |
| | | name="simple-form-number" |
| | | value={phoneNumber} |
| | | onChange={this.handlePhoneNumberChange} |
| | | onChange={this.handlePhoneNumberChange.bind(this)} |
| | | /> |
| | | </FormGroup> |
| | | <ActionGroup> |
| | |
| | | import React from "react"; |
| | | import { List, ListItem } from "@patternfly/react-core"; |
| | | import { List, ListItem, Button, Level, LevelItem } from "@patternfly/react-core"; |
| | | import { ShelterService } from "../Services/ShelterService"; |
| | | import { Shelter } from "../Models/Shelter"; |
| | | import { Link } from "react-router-dom"; |
| | | |
| | | |
| | | type ShelterListProps = { |
| | |
| | | |
| | | return ( |
| | | <List> |
| | | {shelters.map(shelter => <ListItem key={shelter.shelterId}>{shelter.shelterName}</ListItem>)} |
| | | {shelters.map(shelter => <ListItem key={shelter.shelterId}> |
| | | {this.renderShelter(shelter)} |
| | | </ListItem>)} |
| | | </List> |
| | | |
| | | ); |
| | | } |
| | | |
| | | private renderShelter(shelter: Shelter) { |
| | | return ( |
| | | <React.Fragment> |
| | | <Level> |
| | | <LevelItem> |
| | | {shelter.shelterName} |
| | | </LevelItem> |
| | | <LevelItem> |
| | | <Link to={`/shelters/${shelter.shelterId}`}> |
| | | <Button> |
| | | Details |
| | | </Button> |
| | | </Link> |
| | | </LevelItem> |
| | | </Level> |
| | | </React.Fragment> |
| | | ); |
| | | } |
| | | |
| | |
| | | shelterId: string; |
| | | breed: string; |
| | | adoptable: boolean; |
| | | approximateSize: string; |
| | | approximateSize: string; |
| | | residencyRequired: string; |
| | | weight: number; |
| | | squareFootageOfHome: number; |
| | |
| | | export enum Residency { |
| | | APARTMENT, |
| | | HOUSE |
| | | APARTMENT = "APARTMENT", |
| | | HOUSE = "HOUSE" |
| | | } |
| | |
| | | export default class AdoptionRESTService extends RESTService implements AdoptionService { |
| | | |
| | | constructor(baseUrl: string) { |
| | | super(baseUrl, "shelter-service"); |
| | | super(baseUrl, "adoption-service"); |
| | | } |
| | | |
| | | public getAdoptableByShelter(): Promise<Animal[]> { |
| | | return this.get("/getAllAdoptableByShelter"); |
| | | public async getAdoptableByShelter(shelterId: string): Promise<Animal[]> { |
| | | const animalsByShelter = await this.get<Record<string,Animal[]>>( |
| | | "/adoption/getAllAdoptableByShelter" |
| | | ); |
| | | |
| | | for(const key of Object.keys(animalsByShelter)) { |
| | | if (key.includes(`shelterId=${shelterId}`)) { |
| | | return animalsByShelter[key]; |
| | | } |
| | | } |
| | | return []; |
| | | } |
| | | |
| | | public async applyForAdoption(adoptionApplication: AdoptionApplication): Promise<void> { |
| | | await this.post("/applyForAdoption", adoptionApplication); |
| | | await this.post("/adoption/applyForAdoption", adoptionApplication); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | |
| | | export interface AdoptionService { |
| | | getAdoptableByShelter(): Promise<Animal[]>; |
| | | getAdoptableByShelter(shelterId: string): Promise<Animal[]>; |
| | | applyForAdoption(adoptionApplication: AdoptionApplication): Promise<void>; |
| | | } |
| | |
| | | squareFootageOfHome: 800, |
| | | childSafe: true, |
| | | otherDogSafe: true |
| | | }, |
| | | { |
| | | animalId: "a1", |
| | | animalName: "Dog 1", |
| | | breed: "Shepherd", |
| | | shelterId: "s1", |
| | | adoptable: true, |
| | | weight: 100, |
| | | approximateSize: "L", |
| | | residencyRequired: "HOUSE", |
| | | squareFootageOfHome: 800, |
| | | childSafe: true, |
| | | otherDogSafe: true |
| | | }, |
| | | { |
| | | animalId: "a1", |
| | | animalName: "Dog 1", |
| | | breed: "Shepherd", |
| | | shelterId: "s1", |
| | | adoptable: true, |
| | | weight: 100, |
| | | approximateSize: "L", |
| | | residencyRequired: "HOUSE", |
| | | squareFootageOfHome: 800, |
| | | childSafe: true, |
| | | otherDogSafe: true |
| | | }, |
| | | { |
| | | animalId: "a1", |
| | | animalName: "Dog 1", |
| | | breed: "Shepherd", |
| | | shelterId: "s1", |
| | | adoptable: true, |
| | | weight: 100, |
| | | approximateSize: "L", |
| | | residencyRequired: "HOUSE", |
| | | squareFootageOfHome: 800, |
| | | childSafe: true, |
| | | otherDogSafe: true |
| | | } |
| | | ]; |
| | | } |
| | | |
| | | public async getById(id: string): Promise<Animal> { |
| | | public async getById(): Promise<Animal> { |
| | | return { |
| | | animalId: "a1", |
| | | animalName: "Dog 1", |
| | |
| | | export default class AnimalRESTService extends RESTService implements AnimalService { |
| | | |
| | | constructor(baseUrl: string) { |
| | | super(baseUrl, "shelter-service"); |
| | | super(baseUrl, "animal-service"); |
| | | } |
| | | public async create(animal: Animal): Promise<void> { |
| | | await this.post(`/animals/${animal.shelterId}/create`, animal); |
| | | } |
| | | |
| | | public getAllAdoptable(): Promise<Animal[]> { |
| | | return this.get("/animals/getAllAdoptable") |
| | | return this.get("/animals/getAllAdoptable"); |
| | | } |
| | | |
| | | public getById(id: string): Promise<Animal> { |
| | |
| | | const r = await this.axiosInstance.post<T>(url, body); |
| | | return r.data; |
| | | } catch (e) { |
| | | throw new RESTConnectionError(e, this.remoteServiceName, e.response.status); |
| | | throw new RESTConnectionError(e, this.remoteServiceName, e.response?.status); |
| | | } |
| | | } |
| | | |
| | |
| | | import { ShelterService } from "./ShelterService"; |
| | | import { Shelter } from "../Models/Shelter"; |
| | | |
| | | |
| | | export default class ShelterFakeService implements ShelterService { |
| | | |
| | | public async getById(id: string): Promise<Shelter> { |
| | | return { |
| | | shelterId: id, |
| | | shelterName: "A Fake Shelter", |
| | | state: "Minnesota", |
| | | country: "US", |
| | | address: "200 Good Boy Ave", |
| | | email: "frontdesk@minneapolismutts.com", |
| | | phoneNumber: "212-555-9758" |
| | | }; |
| | | } |
| | | |
| | | public async create(): Promise<void> { |
| | | alert("ShelterFakeService: create() was called!"); |
| | | return Promise.resolve(); |
| | | } |
| | | |
| | | public async getAll(): Promise<any[]> { |
| | | public async getAll(): Promise<Shelter[]> { |
| | | return [ |
| | | { shelterId: "s1", shelterName: "Shelter 1" }, |
| | | { shelterId: "s2", shelterName: "Shelter 2" } |
| | | { |
| | | shelterId: "1234", |
| | | shelterName: "A Fake Shelter 1", |
| | | state: "Minnesota", |
| | | country: "US", |
| | | address: "200 Good Boy Ave", |
| | | email: "frontdesk@minneapolismutts.com", |
| | | phoneNumber: "212-555-9758" |
| | | }, |
| | | { |
| | | shelterId: "3456", |
| | | shelterName: "A Fake Shelter 2", |
| | | state: "Minnesota", |
| | | country: "US", |
| | | address: "100 Good Boy Ave", |
| | | email: "frontdesk@minneapolismutts2.com", |
| | | phoneNumber: "212-444-8475" |
| | | } |
| | | ]; |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | public async create(params: ShelterParams): Promise<void> { |
| | | return this.post("/create", params); |
| | | return this.post("/shelters/create", params); |
| | | } |
| | | |
| | | public getAll(): Promise<Array<Shelter>> { |
| | | return this.get<Array<Shelter>>("/shelters/getAll"); |
| | | |
| | | public async getById(id: string): Promise<Shelter> { |
| | | return this.get<Shelter>(`/shelters/${id}/getShelter`); |
| | | } |
| | | |
| | | public getAll(): Promise<Shelter[]> { |
| | | return this.get<Shelter[]>("/shelters/getAll"); |
| | | } |
| | | } |
| | |
| | | import { Shelter } from "../Models/Shelter"; |
| | | |
| | | export type ShelterParams = { |
| | | name: string |
| | | } |
| | |
| | | |
| | | export interface ShelterService { |
| | | create(params: ShelterParams): Promise<void>; |
| | | getAll(): Promise<any[]>; |
| | | getById(id: string): Promise<Shelter> |
| | | getAll(): Promise<Shelter[]>; |
| | | } |
| | | |
| | |
| | | import React from "react"; |
| | | import { PageSection, PageSectionVariants, Text, TextContent } from "@patternfly/react-core"; |
| | | import { PageSection, PageSectionVariants, Text, TextContent, GridItem, Grid } from "@patternfly/react-core"; |
| | | import { AdoptionService } from "../Services/AdoptionService"; |
| | | import { AnimalService } from "../Services/AnimalService"; |
| | | import { Animal } from "../Models/Animal"; |
| | | import AdoptionForm from "../Components/AdoptionForm"; |
| | | |
| | | type AnimalDetailsViewProps = { |
| | | adoptionService: AdoptionService; |
| | | animalService: AnimalService; |
| | | } |
| | | |
| | | export default class AnimalDetailsView extends React.Component<AnimalDetailsViewProps, AnimalService> { |
| | | type AnimalDetailsViewState = { |
| | | animal?: Animal |
| | | } |
| | | |
| | | export default class AnimalDetailsView |
| | | extends React.Component<AnimalDetailsViewProps, AnimalDetailsViewState> { |
| | | |
| | | constructor(props: AnimalDetailsViewProps) { |
| | | super(props); |
| | | this.state = { |
| | | animal: undefined |
| | | }; |
| | | } |
| | | |
| | | |
| | | public async componentDidMount() { |
| | | const { animalId } = this.props.match.params; |
| | | const animal = await this.props.animalService.getById(animalId); |
| | | this.setState({ |
| | | animal |
| | | }); |
| | | } |
| | | |
| | | public render() { |
| | | const { animal } = this.state; |
| | | return animal ? this.renderAnimal(animal) : this.renderMissingAnimal(); |
| | | } |
| | | |
| | | private renderAnimal(animal: Animal) { |
| | | return ( |
| | | <React.Fragment> |
| | | <PageSection variant={PageSectionVariants.light}> |
| | | <TextContent> |
| | | <Text component="h1">{animal.animalName}</Text> |
| | | </TextContent> |
| | | <Grid> |
| | | <GridItem span={4}> |
| | | <img src={`/photos/${animal.animalId}.jpeg`} /> |
| | | </GridItem> |
| | | <GridItem span={8}> |
| | | <TextContent> |
| | | <Text component="p"> |
| | | <strong>Name: </strong>{animal.animalName} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Breed: </strong>{animal.breed} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Weight: </strong>{animal.weight} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Approximate Size: </strong>{animal.approximateSize} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Child Safe: </strong> |
| | | {animal.childSafe ? "Yes" : "No"} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Other Dogs Safe: </strong> |
| | | {animal.otherDogSafe ? "Yes" : "No"} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Residency Required: </strong> |
| | | {animal.residencyRequired ? "Yes" : "No"} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Residency Required: </strong> |
| | | {animal.residencyRequired ? "Yes" : "No"} |
| | | </Text> |
| | | |
| | | <Text component="p"> |
| | | <strong>Square Footage of Home: </strong> |
| | | {animal.squareFootageOfHome} |
| | | </Text> |
| | | </TextContent> |
| | | </GridItem> |
| | | </Grid> |
| | | |
| | | |
| | | </PageSection> |
| | | <PageSection> |
| | | <TextContent> |
| | | <Text component="h2">Adopt {animal.animalName}!</Text> |
| | | </TextContent> |
| | | <AdoptionForm adoptionService={this.props.adoptionService} animalId={animal.animalId} /> |
| | | </PageSection> |
| | | </React.Fragment> |
| | | ); |
| | | } |
| | | |
| | | private renderMissingAnimal() { |
| | | return ( |
| | | <PageSection variant={PageSectionVariants.light}> |
| | | |
| | | <TextContent> |
| | | <Text component="h1">Not Found</Text> |
| | | <Text component="p"> |
| | | This animal does not exist. |
| | | </Text> |
| | | </TextContent> |
| | | </PageSection> |
| | | ) |
| | | ); |
| | | |
| | | } |
| | | |
| | | } |
| | |
| | | import { PageSection, PageSectionVariants, Text, TextContent } from "@patternfly/react-core"; |
| | | import AdoptableAnimalList from "../Components/AdoptableAnimalList"; |
| | | import { AdoptionService } from "../Services/AdoptionService"; |
| | | import { Animal } from "../Models/Animal"; |
| | | |
| | | |
| | | type AnimalsViewProps = { |
| | |
| | | adoptionService: AdoptionService; |
| | | } |
| | | |
| | | type AnimalsViewState = { |
| | | animals: Animal[] |
| | | } |
| | | |
| | | export default class AnimalsView extends React.Component<AnimalsViewProps> { |
| | | |
| | | export default class AnimalsView extends React.Component<AnimalsViewProps, AnimalsViewState> { |
| | | |
| | | constructor(props: AnimalsViewProps) { |
| | | super(props); |
| | | this.state = { |
| | | animals: [] |
| | | }; |
| | | } |
| | | |
| | | public async componentDidMount() { |
| | | const animals = await this.props.animalService.getAllAdoptable(); |
| | | this.setState({ |
| | | animals |
| | | }); |
| | | } |
| | | |
| | | public render() { |
| | | return ( |
| | |
| | | </TextContent> |
| | | </PageSection> |
| | | <PageSection> |
| | | <Text component="h2">Create a Shelter</Text> |
| | | <AdoptableAnimalList |
| | | animalService={this.props.animalService} |
| | | adoptionService={this.props.adoptionService} |
| | | /> |
| | | <Text component="h2">Adoptable Animals</Text> |
| | | <AdoptableAnimalList animals={this.state.animals} /> |
| | | </PageSection> |
| | | </React.Fragment> |
| | | ); |
New file |
| | |
| | | import React from "react"; |
| | | import { PageSection, PageSectionVariants, Text, TextContent } from "@patternfly/react-core"; |
| | | import { ShelterService } from "../Services/ShelterService"; |
| | | import { Shelter } from "../Models/Shelter"; |
| | | import { AdoptionService } from "../Services/AdoptionService"; |
| | | import { Animal } from "../Models/Animal"; |
| | | import AdoptableAnimalList from "../Components/AdoptableAnimalList"; |
| | | |
| | | type ShelterDetailsViewProps = { |
| | | shelterService: ShelterService; |
| | | adoptionService: AdoptionService; |
| | | match: { |
| | | params: { |
| | | shelterId: string |
| | | } |
| | | } |
| | | } |
| | | |
| | | type ShelterDetailsViewState = { |
| | | shelter?: Shelter, |
| | | adoptableAnimals: Animal[] |
| | | } |
| | | |
| | | export default class ShelterDetailsView |
| | | extends React.Component<ShelterDetailsViewProps, ShelterDetailsViewState> { |
| | | |
| | | constructor(props: ShelterDetailsViewProps) { |
| | | super(props); |
| | | this.state = { |
| | | shelter: undefined, |
| | | adoptableAnimals: [] |
| | | }; |
| | | } |
| | | |
| | | |
| | | public async componentDidMount() { |
| | | const { shelterId } = this.props.match.params; |
| | | const [ shelter, adoptableAnimals ] = await Promise.all([ |
| | | this.props.shelterService.getById(shelterId), |
| | | this.props.adoptionService.getAdoptableByShelter(shelterId) |
| | | ]); |
| | | this.setState({ |
| | | shelter, |
| | | adoptableAnimals |
| | | }); |
| | | } |
| | | |
| | | public render() { |
| | | const { shelter } = this.state; |
| | | return shelter ? this.renderShelter(shelter) : this.renderMissingShelter(); |
| | | } |
| | | |
| | | private renderShelter(shelter: Shelter) { |
| | | return ( |
| | | <React.Fragment> |
| | | <PageSection variant={PageSectionVariants.light}> |
| | | <TextContent> |
| | | <Text component="h1"> |
| | | {shelter.shelterName} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Name: </strong>{shelter.shelterName} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>State: </strong>{shelter.state} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Country: </strong>{shelter.country} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Address: </strong>{shelter.address} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Email: </strong>{shelter.email} |
| | | </Text> |
| | | <Text component="p"> |
| | | <strong>Phone Number: </strong>{shelter.phoneNumber} |
| | | </Text> |
| | | </TextContent> |
| | | </PageSection> |
| | | <PageSection> |
| | | <TextContent> |
| | | <Text component="h1"> |
| | | Adoptable Animals |
| | | </Text> |
| | | </TextContent> |
| | | <AdoptableAnimalList animals={this.state.adoptableAnimals}></AdoptableAnimalList> |
| | | </PageSection> |
| | | </React.Fragment> |
| | | ); |
| | | } |
| | | |
| | | private renderMissingShelter() { |
| | | return ( |
| | | <PageSection variant={PageSectionVariants.light}> |
| | | <TextContent> |
| | | <Text component="h1">Not Found</Text> |
| | | <Text component="p"> |
| | | This shelter does not exist. |
| | | </Text> |
| | | </TextContent> |
| | | </PageSection> |
| | | ); |
| | | } |
| | | |
| | | } |