Skip to content

Commit

Permalink
Merge pull request #169 from zvonimirr/feat/due-date-modifier-ui
Browse files Browse the repository at this point in the history
feat: alter due date modifier from the ui
  • Loading branch information
zvonimirr authored Nov 29, 2022
2 parents 071baaf + 7a4c3c4 commit b155469
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 40 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- Remove React Select Currency ([@zvonimirr](https://github.com/zvonimirr))
- Allow adding/removing tenant to a property from the tenant overview page ([@kovaj024](https://github.com/kovaj024))
- Turn home page into stats page ([@zvonimirr](https://github.com/zvonimirr))
- Allow setting the due date modifier from the UI ([@zvonimirr](https://github.com/zvonimirr))

## 0.1.0

Expand Down
2 changes: 1 addition & 1 deletion backend/lib/tenantee/property/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ defmodule Tenantee.Property.Schema do

def changeset(property, attrs) do
property
|> cast(attrs, [:name, :description, :location, :price])
|> cast(attrs, [:name, :description, :location, :price, :due_date_modifier])
|> cast_assoc(:tenants)
|> validate_required([:name, :location, :price])
end
Expand Down
25 changes: 22 additions & 3 deletions backend/lib/tenantee_web/controllers/property.ex
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,17 @@ defmodule TenanteeWeb.PropertyController do
with true <- Map.keys(params) -- ["id"] != [],
currency <- Map.get(params, "currency", "USD"),
:ok <- Currency.valid?(currency),
property_params <-
Map.replace_lazy(params, "price", fn price -> Money.new(price, currency) end)
|> Map.delete("id"),
price <- Map.get(params, "price"),
{:ok, updated_price} <- get_updated_price(price, currency),
property_params <- Map.replace(params, "price", updated_price),
property_params <- Map.delete(property_params, "id"),
{:ok, _property} <- Property.get_property(id),
{:ok, updated_property} <- Property.update_property(id, property_params) do
render(conn, "show.json", %{property: updated_property})
else
{:error, "Invalid price"} ->
respond(conn, :unprocessable_entity, "Invalid price")

{:error, "Invalid currency"} ->
respond(conn, :unprocessable_entity, "Invalid currency")

Expand Down Expand Up @@ -117,4 +121,19 @@ defmodule TenanteeWeb.PropertyController do
{:error, :not_found} -> respond(conn, :not_found, "Property not found")
end
end

defp get_updated_price(price, currency) when is_binary(price) do
case price |> Decimal.parse() do
:error -> {:error, "Invalid price"}
{updated_price, _junk} -> {:ok, Money.from_float(Decimal.to_float(updated_price), currency)}
end
end

defp get_updated_price(price, currency) when is_number(price) do
if is_float(price) do
{:ok, Money.from_float(price, currency)}
else
{:ok, Money.new(price, currency)}
end
end
end
32 changes: 30 additions & 2 deletions backend/test/tenantee_web/controllers/property_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,21 @@ defmodule TenanteeWeb.PropertyControllerTest do
"name" => "Updated Property",
"location" => "Updated Location",
"currency" => "USD",
"price" => 1000
"price" => "1000"
})

assert json_response(conn, 200)["name"] == "Updated Property"
end

test "happy path (float price)", %{conn: conn} do
%{id: id} = Tenantee.Factory.Property.insert()

conn =
patch(conn, "/api/properties/#{id}", %{
"name" => "Updated Property",
"location" => "Updated Location",
"currency" => "USD",
"price" => 10.50
})

assert json_response(conn, 200)["name"] == "Updated Property"
Expand All @@ -79,12 +93,26 @@ defmodule TenanteeWeb.PropertyControllerTest do
"name" => "Updated Property",
"location" => "Updated Location",
"currency" => "invalid",
"price" => 1000
"price" => 1000.52
})

assert json_response(conn, 422)["message"] == "Invalid currency"
end

test "invalid price", %{conn: conn} do
%{id: id} = Tenantee.Factory.Property.insert()

conn =
patch(conn, "/api/properties/#{id}", %{
"name" => "Updated Property",
"location" => "Updated Location",
"currency" => "USD",
"price" => "invalid"
})

assert json_response(conn, 422)["message"] == "Invalid price"
end

test "invalid params", %{conn: conn} do
%{id: id} = Tenantee.Factory.Property.insert()

Expand Down
5 changes: 5 additions & 0 deletions frontend/cypress/component/Cards/PropertyCard.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ describe('PropertyCard', () => {
currency: 'USD',
},
tenants: [],
monthly_revenue: {
amount: 100,
currency: 'USD',
},
due_date_modifier: 0,
}}
/>,
);
Expand Down
3 changes: 3 additions & 0 deletions frontend/cypress/component/Cards/TenantCard.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe('TenantCard', () => {
id: 1,
name: 'Test Tenant',
unpaid_rents: [],
properties: [],
}}
/>,
);
Expand Down Expand Up @@ -50,6 +51,7 @@ describe('TenantCard', () => {
due_date,
},
],
properties: [],
}}
/>,
);
Expand Down Expand Up @@ -78,6 +80,7 @@ describe('TenantCard', () => {
due_date,
},
],
properties: [],
}}
/>,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('AddPropertyModal', () => {
cy.get('#submit').should('be.disabled');

cy.get('input').each((input) => {
cy.wrap(input).type('123');
cy.wrap(input).type('12');
});

cy.get('#submit').should('not.be.disabled');
Expand Down Expand Up @@ -45,17 +45,18 @@ describe('AddPropertyModal', () => {
);

cy.get('input').each((input) => {
cy.wrap(input).type('123');
cy.wrap(input).type('12');
});

cy.get('#submit').click();

cy.wrap(onSubmit).should('have.been.calledWith', {
name: '123',
description: '123',
location: '123',
price: 123,
name: '12',
description: '12',
location: '12',
price: 12,
currency: 'USD',
due_date_modifier: 12 * 60 * 60 * 24,
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ const mockProperty = {
currency: 'USD',
},
tenants: [],
monthly_revenue: {
amount: 100,
currency: 'USD',
},
due_date_modifier: 0,
};

describe('EditPropertyModal', () => {
Expand All @@ -26,7 +31,7 @@ describe('EditPropertyModal', () => {
cy.get('#submit').should('be.disabled');

cy.get('input').each((input) => {
cy.wrap(input).type('123');
cy.wrap(input).type('12');
});

cy.get('#submit').should('not.be.disabled');
Expand Down Expand Up @@ -67,6 +72,7 @@ describe('EditPropertyModal', () => {
price: 100,
location: 'Test Location',
currency: 'USD',
due_date_modifier: 0,
});
});
});
31 changes: 29 additions & 2 deletions frontend/src/components/Property/Modals/AddPropertyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import {
ModalHeader,
ModalOverlay,
Stack,
Text,
} from '@chakra-ui/react';
import { IconHome, IconPencil } from '@tabler/icons';
import { IconCalendar, IconHome, IconPencil } from '@tabler/icons';
import { useForm } from 'react-hook-form';
import { PropertyDto } from '../../../types/property';
import { calculateDueDateModifier, PropertyDto } from '../../../types/property';
import GenericInput from '../../Form/GenericInput';
import getSymbolFromCurrency from 'currency-symbol-map';
import CurrencySelect from '../../Form/CurrencySelect';
Expand All @@ -29,6 +30,7 @@ const defaultValues = {
price: 0,
location: '',
currency: 'USD',
due_date_modifier: 0,
};

function AddPropertyModal({
Expand Down Expand Up @@ -97,6 +99,25 @@ function AddPropertyModal({
setValue('currency', value)
}
/>
<GenericInput
name="due_date_modifier"
label="Due Date Modifier"
placeholder="Due Date Modifier"
control={control}
type="number"
rules={{
required: 'Due Date Modifier is required',
min: 0,
max: 25,
}}
leftAdornment={<IconCalendar />}
/>
<Text fontSize="sm">
Due date modifier is used when calculating the
due date for a payment. It is the number of days
after the start of the month that the payment is
due. (Min: 0, Max: 25)
</Text>
<Box w="100%">
<Button
id="submit"
Expand All @@ -110,6 +131,12 @@ function AddPropertyModal({
onSubmit({
...values,
price: Number(values.price),
due_date_modifier:
calculateDueDateModifier(
Number(
values.due_date_modifier,
),
),
}),
)}>
Add Property
Expand Down
37 changes: 35 additions & 2 deletions frontend/src/components/Property/Modals/EditPropertyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ import {
ModalHeader,
ModalOverlay,
Stack,
Text,
} from '@chakra-ui/react';
import { IconHome, IconPencil } from '@tabler/icons';
import { IconCalendar, IconHome, IconPencil } from '@tabler/icons';
import { useForm } from 'react-hook-form';
import { Property, PropertyUpdateDto } from '../../../types/property';
import {
calculateDueDateModifier,
getNumberOfDaysFromDueDateModifier,
Property,
PropertyUpdateDto,
} from '../../../types/property';
import GenericInput from '../../Form/GenericInput';
import { useEffect } from 'react';
import getSymbolFromCurrency from 'currency-symbol-map';
Expand All @@ -31,6 +37,7 @@ const defaultValues = {
price: 0,
location: '',
currency: '',
due_date_modifier: 0,
};

function EditPropertyModal({
Expand All @@ -53,6 +60,9 @@ function EditPropertyModal({
price: property.price.amount,
location: property.location,
currency: property.price.currency,
due_date_modifier: getNumberOfDaysFromDueDateModifier(
property.due_date_modifier,
),
});
}
}, [property]);
Expand Down Expand Up @@ -113,6 +123,25 @@ function EditPropertyModal({
setValue('currency', value)
}
/>
<GenericInput
name="due_date_modifier"
label="Due Date Modifier"
placeholder="Due Date Modifier"
control={control}
type="number"
rules={{
required: 'Due Date Modifier is required',
min: 0,
max: 25,
}}
leftAdornment={<IconCalendar />}
/>
<Text fontSize="sm">
Due date modifier is used when calculating the
due date for a payment. It is the number of days
after the start of the month that the payment is
due. (Min: 0, Max: 25)
</Text>
<Box w="100%">
<Button
id="submit"
Expand All @@ -128,6 +157,10 @@ function EditPropertyModal({
...values,
price: Number(values.price),
id: property?.id ?? 0,
due_date_modifier:
calculateDueDateModifier(
values.due_date_modifier,
),
}),
)}>
Update Property
Expand Down
16 changes: 1 addition & 15 deletions frontend/src/pages/Tenant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,7 @@ function TenantPage() {
[tenantError],
);

const {
data: allPropertiesData,
error: allPropertiesError,
isValidating: isValidatingAllProperties,
} = useSWR<PropertyList>(
const { data: allPropertiesData } = useSWR<PropertyList>(
PropertyApiService.listPropertiesPath,
propertyApiService.getProperties,
);
Expand All @@ -73,16 +69,6 @@ function TenantPage() {
() => allPropertiesData?.properties,
[allPropertiesData],
);
const isAllPropertiesError = useMemo(
() => allPropertiesError !== undefined,
[allPropertiesError],
);
const isLoadingAllProperties = useMemo(
() =>
allProperties !== undefined ||
(isValidatingAllProperties && isAllPropertiesError),
[allProperties, isValidatingAllProperties, isAllPropertiesError],
);

const {
data: rentData,
Expand Down
Loading

0 comments on commit b155469

Please sign in to comment.