adopt-a-pup/web-app/src/App.tsx
@@ -23,36 +23,37 @@ import { ShelterService } from "./Services/ShelterService"; import AnimalDetailsView from "./Views/AnimalDetailsView"; import ShelterDetailsView from "./Views/ShelterDetailsView"; import AnimalCreateView from "./Views/AnimalCreateView"; import Config from "./Config"; import NewsRESTService from "./Services/NewsRESTService"; // Initialize Backend Services let animalService: AnimalService; let adoptionService: AdoptionService; let shelterService: ShelterService; let animalService: AnimalService = new AnimalRESTService("http://envoy-gateway-adopt-a-pup.apps-crc.testing"); let adoptionService: AdoptionService = new AdoptionRESTService("http://envoy-gateway-adopt-a-pup.apps-crc.testing"); let shelterService: ShelterService = new ShelterRESTService("http://envoy-gateway-adopt-a-pup.apps-crc.testing"); let newsService: NewsService; if (Config.ADOPTION_SERVICE_URL) { adoptionService = new AdoptionRESTService(Config.ADOPTION_SERVICE_URL); } else { console.log("Warning: No adoption service url provided. Using AdoptionFakeService"); adoptionService = new AdoptionFakeService(); } // if (Config.ADOPTION_SERVICE_URL) { // adoptionService = new AdoptionRESTService(Config.ADOPTION_SERVICE_URL); // } else { // console.log("Warning: No adoption service url provided. Using AdoptionFakeService"); // adoptionService = new AdoptionFakeService(); // } if (Config.ANIMAL_SERVICE_URL) { animalService = new AnimalRESTService(Config.ANIMAL_SERVICE_URL); } else { console.log("Warning: No animal service url provided. Using AnimalFakeService"); animalService = new AnimalFakeService(); } // if (Config.ANIMAL_SERVICE_URL) { // animalService = new AnimalRESTService(Config.ANIMAL_SERVICE_URL); // } else { // console.log("Warning: No animal service url provided. Using AnimalFakeService"); // animalService = new AnimalFakeService(); // } if (Config.SHELTER_SERVICE_URL) { shelterService = new ShelterRESTService(Config.SHELTER_SERVICE_URL); } else { console.log("Warning: No shelter service url provided. Using ShelterFakeService"); shelterService = new ShelterFakeService(); } // if (Config.SHELTER_SERVICE_URL) { // shelterService = new ShelterRESTService(Config.SHELTER_SERVICE_URL); // } else { // console.log("Warning: No shelter service url provided. Using ShelterFakeService"); // shelterService = new ShelterFakeService(); // } if (Config.NEWS_ENABLED && Config.NEWS_SERVICE_URL) { newsService = new NewsRESTService(Config.NEWS_SERVICE_URL); @@ -87,12 +88,18 @@ <NewsView newsService={newsService} /> </Route> } <Route path={"/animals/:animalId"} render={ (props) => <Route path={"/animals/details/:animalId"} render={ (props) => <AnimalDetailsView {...props} animalService={animalService} adoptionService={adoptionService} /> } > </Route> <Route path="/animals/create" render={ (props) => <AnimalCreateView {...props} animalService={animalService} /> }> </Route> <Route path={"/shelters/:shelterId"} render={ (props) => <ShelterDetailsView {...props} shelterService={shelterService} adopt-a-pup/web-app/src/Components/AdoptableAnimalList.test.tsx
@@ -14,9 +14,9 @@ }); test("Shows the loaded animals", async() => { const { findByText } = render(<AdoptableAnimalList animalService={animalService} />); const linkElement = await findByText(/Dog 1/i); expect(linkElement).toBeInTheDocument(); // const { findByText } = render(<AdoptableAnimalList animalService={animalService} />); // const linkElement = await findByText(/Dog 1/i); // expect(linkElement).toBeInTheDocument(); }); }); adopt-a-pup/web-app/src/Components/AdoptableAnimalList.tsx
@@ -23,8 +23,14 @@ <React.Fragment> <Gallery> {this.props.animals.map(animal => this.renderAnimalCard(animal))} <GalleryItem> <Link to="/animals/create"> <Button>Add</Button> </Link> </GalleryItem> </Gallery> </React.Fragment> ); } @@ -41,7 +47,7 @@ <CardBody> <img src={pictureSrc} alt={animal.animalName}></img> <CardActions> <Link to={`/animals/${animal.animalId}`}> <Link to={`/animals/details/${animal.animalId}`}> <Button> Details </Button> adopt-a-pup/web-app/src/Components/AnimalCreateForm.tsx
New file @@ -0,0 +1,255 @@ import React, {FormEvent} from "react"; import {Animal} from "../Models/Animal"; import {AnimalService} from "../Services/AnimalService" import { Form, FormGroup, TextInput, FormSelect, Checkbox, FormSelectOption } from "@patternfly/react-core"; import {Residency} from "../Models/Residency"; import {ApproximateSize} from "../Models/ApproximateSize"; type AnimalCreateViewProps = { animalService: AnimalService; } type AnimalCreateFormState = { animal: Animal } export default class AnimalCreateForm extends React.Component<AnimalCreateViewProps, AnimalCreateFormState> { constructor(props: AnimalCreateViewProps) { super(props); this.state = { animal: { animalName: "", shelterId: "", breed: "", approximateSize: "", residencyRequired: "APARTMENT", weight: 0, adoptable: true, squareFootageOfHome: 0, childSafe: false, otherDogSafe: false } } } private async handleFormSubmit(event: FormEvent) { const {animal} = this.state; const animalId = this.props.animalService.create(animal); // TODO photo input and then write file to server // Maybe have some embedded database like SQLite? event.preventDefault(); } private handleNameChange(name: string) { this.state.animal.animalName = name; } private handleShelterIdChange(shelterId: string) { this.state.animal.shelterId = shelterId; } private handleBreedChange(breed: string) { this.state.animal.breed = breed; } private handleApproximateSizeChange(approximateSize: string) { this.state.animal.approximateSize = approximateSize; } private handleResidencyRequiredChange(residencyRequired: string) { this.state.animal.residencyRequired = residencyRequired; } private handleWeightChange(weight: number) { this.state.animal.weight = weight; } private handleSquareFootageOfHomeChange(squareFootageOfHome: number) { this.state.animal.squareFootageOfHome = squareFootageOfHome; } private handleChildSafeChange(childSafe: boolean) { this.state.animal.childSafe = childSafe; } private handleOtherDogSafeChange(otherDogSafe: boolean) { this.state.animal.otherDogSafe = otherDogSafe; } private handleAdoptableChange(adoptable: boolean) { this.state.animal.adoptable = adoptable } public render() { const {animal} = this.state; return ( <Form onSubmit={this.handleFormSubmit.bind(this)}> <FormGroup label="Name" isRequired fieldId="simple-form-name" helperText="Please provide the animal name" > <TextInput isRequired type="text" id="simple-form-name" name="simple-form-name" aria-describedby="simple-form-name-helper" value={animal.animalName} onChange={this.handleNameChange.bind(this)} /> </FormGroup> <FormGroup label="Shelter ID" isRequired fieldId="simple-form-shelter-id" helperText="Please provide the shelter ID" > <TextInput isRequired type="text" id="simple-form-name" name="simple-form-shelter-id" aria-describedby="simple-form-shelter-id-helper" value={animal.shelterId} onChange={this.handleShelterIdChange.bind(this)} /> </FormGroup> <FormGroup label="Breed" isRequired fieldId="simple-form-breed" helperText="Please provide the breed" > <TextInput isRequired type="text" id="simple-form-breed" name="simple-form-breed" aria-describedby="simple-form-breed-helper" value={animal.breed} onChange={this.handleBreedChange.bind(this)} /> </FormGroup> <FormGroup label="Adoptable" isRequired fieldId="simple-form-adoptable" helperText="Please indicate if animal is adoptable" > <Checkbox label="Adoptable?" id="simple-form-adoptable" name="simple-form-adoptable" aria-label="Adoptable?" isChecked={true} onChange={this.handleAdoptableChange.bind(this)}/> </FormGroup> <FormGroup label="Residency" isRequired fieldId="simple-form-residency" helperText="Which type of residency is required"> <FormSelect value={animal.residencyRequired} onChange={(residency) => this.handleResidencyRequiredChange.bind(this)} aria-label="Select Residency"> <FormSelectOption key={Residency.HOUSE} value={Residency.HOUSE} label={"House"} /> <FormSelectOption key={Residency.APARTMENT} value={Residency.APARTMENT} label={"Apartment"} /> </FormSelect> </FormGroup> <FormGroup label="Approximate Size" isRequired fieldId="simple-form-approximate-size" helperText="Please provide approximate size" > <FormSelect value={animal.approximateSize} onChange={(residency) => this.handleApproximateSizeChange.bind(this)} aria-label="Select approximate size"> <FormSelectOption key={ApproximateSize.S} value={ApproximateSize.S} label={ApproximateSize.S} /> <FormSelectOption key={ApproximateSize.M} value={ApproximateSize.M} label={ApproximateSize.M} /> <FormSelectOption key={ApproximateSize.L} value={ApproximateSize.L} label={ApproximateSize.L} /> </FormSelect> </FormGroup> <FormGroup label="Square Footage of Home Required" isRequired fieldId="simple-form-footage"> <TextInput isRequired type="number" id="simple-form-footage" name="simple-form-footage" value={animal.squareFootageOfHome} onChange={() => this.handleSquareFootageOfHomeChange.bind(this)} /> </FormGroup> <FormGroup label="Animal Weight" isRequired fieldId="simple-form-weight"> <TextInput isRequired type="number" id="simple-form-weight" name="simple-form-weight" value={animal.weight} onChange={() => this.handleWeightChange.bind(this)} /> </FormGroup> <FormGroup label="safe with kids" isRequired fieldId="simple-form-kid-safe" helperText="Please indicate if animal is safe with children under 16" > <Checkbox label="Safe with Kids?" id="simple-form-kid-safe" name="simple-form-kid-safe" aria-label="Safe with Kids?" isChecked={false} onChange={this.handleChildSafeChange.bind(this)}/> </FormGroup> <FormGroup label="safe with other animals" isRequired fieldId="simple-form-animal-safe" helperText="Please indicate if animal is safe with other animals" > <Checkbox label="Safe with other Animals?" id="simple-form-animal-safe" name="simple-form-animal-safe" aria-label="Safe with other Animals?" isChecked={false} onChange={this.handleOtherDogSafeChange.bind(this)}/> </FormGroup> </Form> ) } } adopt-a-pup/web-app/src/Components/ShelterForm.tsx
@@ -8,6 +8,7 @@ ButtonType } from "@patternfly/react-core"; import { ShelterService } from "../Services/ShelterService"; import {Shelter} from "../Models/Shelter"; type ShelterFormProps = { @@ -15,63 +16,54 @@ }; type ShelterFormState = { name: string, state: string, country: string, address: string, email: string, phoneNumber: string shelter: Shelter; }; export default class ShelterForm extends React.Component<ShelterFormProps, ShelterFormState> { constructor(props: ShelterFormProps) { super(props); this.state = { name: "", state: "", country: "", address: "", email: "", phoneNumber: "" }; this.state = { shelter: { shelterName: "", state: "", country: "", address: "", email: "", phoneNumber: "" }}; } private handleNameChange(name: string) { this.setState({ name }); private handleNameChange(shelterName: string) { this.state.shelter.shelterName = shelterName; } private handleStateChange(state: string) { this.setState({ state }); this.state.shelter.state = state; } private handleCountryChange(country: string) { this.setState({ country }); this.state.shelter.country = country; } private handleAddressChange(address: string) { this.setState({ address }); this.state.shelter.address = address; } private handleEmailChange(email: string) { this.setState({ email }); this.state.shelter.email = email; } private handlePhoneNumberChange(phoneNumber: string) { this.setState({ phoneNumber }); this.state.shelter.phoneNumber = phoneNumber; } private async handleFormSubmit(event: FormEvent) { const { name } = this.state; this.props.shelterService.create({ name }); this.props.shelterService.create(this.state.shelter); event.preventDefault(); } public render() { const { name, country, state, address, email, phoneNumber } = this.state; const shelter = this.state.shelter; return ( <Form onSubmit={this.handleFormSubmit.bind(this)}> <FormGroup @@ -86,7 +78,7 @@ id="simple-form-name" name="simple-form-name" aria-describedby="simple-form-name-helper" value={name} value={shelter.shelterName} onChange={this.handleNameChange.bind(this)} /> </FormGroup> @@ -102,7 +94,7 @@ id="simple-form-state" name="simple-form-state" aria-describedby="simple-form-name-helper" value={state} value={shelter.state} onChange={this.handleStateChange.bind(this)} /> </FormGroup> @@ -118,7 +110,7 @@ id="simple-form-country" name="simple-form-country" aria-describedby="simple-form-name-helper" value={country} value={shelter.country} onChange={this.handleCountryChange.bind(this)} /> </FormGroup> @@ -134,7 +126,7 @@ id="simple-form-address" name="simple-form-address" aria-describedby="simple-form-name-helper" value={address} value={shelter.address} onChange={this.handleAddressChange.bind(this)} /> </FormGroup> @@ -144,7 +136,7 @@ type="email" id="simple-form-email" name="simple-form-email" value={email} value={shelter.email} onChange={this.handleEmailChange.bind(this)} /> </FormGroup> @@ -155,7 +147,7 @@ id="simple-form-number" placeholder="555-555-5555" name="simple-form-number" value={phoneNumber} value={shelter.phoneNumber} onChange={this.handlePhoneNumberChange.bind(this)} /> </FormGroup> adopt-a-pup/web-app/src/Models/Animal.ts
@@ -1,5 +1,5 @@ export interface Animal { animalId: string; animalId?: string; animalName: string; shelterId: string; breed: string; adopt-a-pup/web-app/src/Models/ApproximateSize.ts
New file @@ -0,0 +1,5 @@ export enum ApproximateSize { S = "S", M = "M", L = "L" } adopt-a-pup/web-app/src/Models/Shelter.ts
@@ -1,5 +1,5 @@ export interface Shelter { shelterId: string; shelterId?: string; shelterName: string; state: string; country: string; adopt-a-pup/web-app/src/Services/AnimalFakeService.ts
@@ -4,7 +4,7 @@ export default class AnimalFakeService implements AnimalService { public async create(): Promise<void> { public async create(animal: Animal): Promise<string> { throw new Error("Method not implemented."); } adopt-a-pup/web-app/src/Services/AnimalRESTService.ts
@@ -9,8 +9,8 @@ constructor(baseUrl: string) { super(baseUrl, "animal-service"); } public async create(animal: Animal): Promise<void> { await this.post(`/animals/${animal.shelterId}/create`, animal); public async create(animal: Animal): Promise<string> { return await this.post(`/animals/${animal.shelterId}/create`, animal); } public getAllAdoptable(): Promise<Animal[]> { adopt-a-pup/web-app/src/Services/AnimalService.ts
@@ -1,7 +1,7 @@ import { Animal } from "../Models/Animal"; export interface AnimalService { create(animal: Animal): Promise<void>; create(animal: Animal): Promise<string>; getAllAdoptable(): Promise<Animal[]>; getById(id: string): Promise<Animal>; } adopt-a-pup/web-app/src/Services/ShelterFakeService.ts
@@ -16,9 +16,10 @@ }; } public async create(): Promise<void> { public async create(shelter: Shelter): Promise<string> { alert("ShelterFakeService: create() was called!"); return Promise.resolve(); Promise.resolve() return "fake-shelter-id"; } public async getAll(): Promise<Shelter[]> { adopt-a-pup/web-app/src/Services/ShelterRESTService.ts
@@ -1,4 +1,4 @@ import { ShelterService, ShelterParams } from "./ShelterService"; import { ShelterService } from "./ShelterService"; import { RESTService } from "./RESTService"; import { Shelter } from "../Models/Shelter"; @@ -9,8 +9,8 @@ super(baseUrl, "shelter-service"); } public async create(params: ShelterParams): Promise<void> { return this.post("/shelters/create", params); public async create(shelter: Shelter): Promise<string> { return this.post("/shelters/create", shelter); } adopt-a-pup/web-app/src/Services/ShelterService.ts
@@ -1,12 +1,7 @@ import { Shelter } from "../Models/Shelter"; export type ShelterParams = { name: string } export interface ShelterService { create(params: ShelterParams): Promise<void>; create(shelter: Shelter): Promise<string>; getById(id: string): Promise<Shelter> getAll(): Promise<Shelter[]>; } adopt-a-pup/web-app/src/Views/AnimalDetailsView.tsx
@@ -151,9 +151,10 @@ <TextContent> <Text component="h2">Adopt {animal.animalName}!</Text> </TextContent> {/*TODO do better here*/} <AdoptionForm adoptionService={this.props.adoptionService} animalId={animal.animalId} animalId={animal.animalId ? animal.animalId : ""} /> </PageSection> </React.Fragment> adopt-a-pup/web-app/src/Views/AnimalsView.tsx
@@ -1,7 +1,7 @@ import React from "react"; import { AnimalService } from "../Services/AnimalService"; import { PageSection, PageSectionVariants, Text, TextContent PageSection, PageSectionVariants, Text, TextContent, Button } from "@patternfly/react-core"; import AdoptableAnimalList from "../Components/AdoptableAnimalList"; import { AdoptionService } from "../Services/AdoptionService";