Skip to content

Commit

Permalink
Implement MobX state & action storage
Browse files Browse the repository at this point in the history
  • Loading branch information
fatihkabakk committed May 29, 2022
1 parent 9c32b36 commit ad4ece5
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 76 deletions.
Binary file modified API/reactivities.db
Binary file not shown.
43 changes: 43 additions & 0 deletions client-app/package-lock.json

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

2 changes: 2 additions & 0 deletions client-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"@types/react": "^17.0.27",
"@types/react-dom": "^17.0.9",
"axios": "^0.22.0",
"mobx": "^6.6.0",
"mobx-react-lite": "^3.4.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
Expand Down
50 changes: 14 additions & 36 deletions client-app/src/app/layout/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,23 @@ import ActivityDashboard from '../../features/activities/dashboard/ActivityDashb
import { v4 as uuid } from 'uuid';
import agent from '../api/agent';
import LoadingComponent from './LoadingComponent';
import { useStore } from '../stores/store';
import { observer } from 'mobx-react-lite';


const App = () => {
const { activityStore } = useStore();

export default function App() {
const [activities, setActivities] = useState<Activity[]>([]);
const [selectedActivity, setSelectedActivity] = useState<Activity | undefined>(undefined);
const [editMode, setEditMode] = useState(false);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);

const handleActivitySelect = (id: string) => {
setSelectedActivity(activities.find(a => a.id === id));
}
useEffect(() => {
activityStore.loadActivities();
}, [activityStore])

const handleCancelSelectActivity = () => {
setSelectedActivity(undefined);
}

const handleFormOpen = (id?: string) => {
id ? handleActivitySelect(id) : handleCancelSelectActivity();
setEditMode(true);
}

const handleFormClose = () => {
setEditMode(false);
}

const handleCreateOrEditActivity = (activity: Activity) => {
setSubmitting(true);
Expand Down Expand Up @@ -59,32 +52,15 @@ export default function App() {
});
}

useEffect(() => {
agent.Activities.list().then(response => {
let activities: Activity[] = [];
response.forEach(a => {
a.date = a.date.split('T')[0];
activities.push(a);
});
setActivities(activities);
setLoading(false);
})
}, [])

if (loading) return <LoadingComponent content='Loading app...' />
if (activityStore.loadingInitial) return <LoadingComponent content='Loading app...' />

return (
<>
<NavBar openForm={handleFormOpen} />
<NavBar />
<Container style={{ marginTop: '7em' }}>
<ActivityDashboard
selectedActivity={selectedActivity}
selectActivity={handleActivitySelect}
cancelSelectActivity={handleCancelSelectActivity}
activities={activities}
editMode={editMode}
openForm={handleFormOpen}
closeForm={handleFormClose}
activities={activityStore.activities}
createOrEdit={handleCreateOrEditActivity}
deleteActivity={handleDeleteActivity}
submitting={submitting}
Expand All @@ -93,3 +69,5 @@ export default function App() {
</>
);
}

export default observer(App);
9 changes: 4 additions & 5 deletions client-app/src/app/layout/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from "react";
import { Button, Container, Menu } from "semantic-ui-react";
import { useStore } from "../stores/store";

interface Props {
openForm: () => void;
}
export default function NavBar() {
const { activityStore } = useStore();

export default function NavBar({ openForm }: Props) {
return (
<Menu inverted fixed='top'>
<Container>
Expand All @@ -15,7 +14,7 @@ export default function NavBar({ openForm }: Props) {
</Menu.Item>
<Menu.Item name='Activities' />
<Menu.Item>
<Button onClick={openForm} positive content='Create Activity' />
<Button onClick={() => activityStore.openForm()} positive content='Create Activity' />
</Menu.Item>
</Container>
</Menu>
Expand Down
51 changes: 51 additions & 0 deletions client-app/src/app/stores/activityStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Activity } from './../models/activity';
import { makeAutoObservable } from "mobx";
import agent from '../api/agent';

export default class ActivityStore {
activities: Activity[] = [];
selectedActivity: Activity | undefined = undefined;
editMode = false;
loading = false;
loadingInitial = false;

constructor() {
makeAutoObservable(this);
}

loadActivities = async () => {
this.setLoadingInitial(true);
try {
const activities = await agent.Activities.list();
activities.forEach(a => {
a.date = a.date.split('T')[0];
this.activities.push(a);
});
this.setLoadingInitial(false);
} catch (error) {
console.log(error);
this.setLoadingInitial(false);
}
}

setLoadingInitial = (state: boolean) => {
this.loadingInitial = state;
}

selectActivity = (id: string) => {
this.selectedActivity = this.activities.find(a => a.id === id);
}

cancelSelectedActivity = () => {
this.selectedActivity = undefined;
}

openForm = (id?: string) => {
id ? this.selectActivity(id) : this.cancelSelectedActivity();
this.editMode = true;
}

closeForm = () => {
this.editMode = false;
}
}
16 changes: 16 additions & 0 deletions client-app/src/app/stores/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createContext, useContext } from "react";
import ActivityStore from "./activityStore";

interface Store {
activityStore: ActivityStore;
}

export const store: Store = {
activityStore: new ActivityStore(),
}

export const StoreContext = createContext(store);

export function useStore() {
return useContext(StoreContext);
}
27 changes: 9 additions & 18 deletions client-app/src/features/activities/dashboard/ActivityDashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,40 @@
import React from "react";
import { Grid } from "semantic-ui-react";
import { Activity } from "../../../app/models/activity";
import { useStore } from "../../../app/stores/store";
import ActivityDetails from "../details/ActivityDetails";
import ActivityForm from "../form/ActivityForm";
import ActivityList from "./ActivityList";
import { observer } from "mobx-react-lite";

interface Props {
selectedActivity: Activity | undefined;
selectActivity: (id: string) => void;
cancelSelectActivity: () => void;
activities: Activity[];
editMode: boolean;
openForm: (id: string) => void;
closeForm: () => void;
createOrEdit: (activity: Activity) => void;
deleteActivity: (id: string) => void;
submitting: boolean;
}

export default function ActivityDashboard({ activities, selectedActivity, selectActivity, cancelSelectActivity, editMode, openForm, closeForm, createOrEdit, deleteActivity, submitting }: Props) {
export default observer(function ActivityDashboard({ activities, createOrEdit, deleteActivity, submitting }: Props) {
const { activityStore } = useStore();
const { selectedActivity, editMode } = activityStore;

return (
<Grid>
<Grid.Column width='10'>
<ActivityList
activities={activities}
selectActivity={selectActivity}
deleteActivity={deleteActivity}
submitting={submitting}
/>
</Grid.Column>
<Grid.Column width='6'>
{!editMode && selectedActivity &&
<ActivityDetails
activity={selectedActivity}
cancelSelectActivity={cancelSelectActivity}
openForm={openForm}
/>}
{editMode &&
<ActivityDetails />}
{activityStore.editMode &&
<ActivityForm
createOrEdit={createOrEdit}
activity={selectedActivity}
closeForm={closeForm}
submitting={submitting}
/>}
</Grid.Column>
</Grid>
)
}
})
8 changes: 4 additions & 4 deletions client-app/src/features/activities/dashboard/ActivityList.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, { SyntheticEvent, useState } from 'react'
import { Button, Item, Label, Segment } from 'semantic-ui-react';
import { Activity } from '../../../app/models/activity';
import { useStore } from '../../../app/stores/store';

interface Props {
activities: Activity[];
selectActivity: (id: string) => void;
deleteActivity: (id: string) => void;
submitting: boolean;
}

export default function ActivityList({ activities, selectActivity, deleteActivity, submitting }: Props) {
export default function ActivityList({ deleteActivity, submitting }: Props) {
const { activityStore } = useStore();
const { activities, selectActivity } = activityStore;
const [target, setTarget] = useState('');

const handleActivityDelete = (e: SyntheticEvent<HTMLButtonElement>, id: string) => {
Expand Down
16 changes: 8 additions & 8 deletions client-app/src/features/activities/details/ActivityDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from 'react'
import { Card, Image, Button } from 'semantic-ui-react'
import { Activity } from '../../../app/models/activity'
import LoadingComponent from '../../../app/layout/LoadingComponent';
import { useStore } from '../../../app/stores/store';

interface Props {
activity: Activity;
cancelSelectActivity: () => void;
openForm: (id: string) => void;
}
export default function ActivityDetails() {
const { activityStore } = useStore();
const { selectedActivity: activity, openForm, cancelSelectedActivity } = activityStore;

if (!activity) return <LoadingComponent />;

export default function ActivityDetails({ activity, cancelSelectActivity, openForm }: Props) {
return (
<Card fluid>
<Image src={`/assets/categoryImages/${activity.category}.jpg`} />
Expand All @@ -24,7 +24,7 @@ export default function ActivityDetails({ activity, cancelSelectActivity, openFo
<Card.Content extra>
<Button.Group widths='2'>
<Button onClick={() => { openForm(activity.id) }} basic color='blue' content='Edit' />
<Button onClick={cancelSelectActivity} basic color='grey' content='Cancel' />
<Button onClick={cancelSelectedActivity} basic color='grey' content='Cancel' />
</Button.Group>
</Card.Content>
</Card>
Expand Down
7 changes: 4 additions & 3 deletions client-app/src/features/activities/form/ActivityForm.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React, { ChangeEvent, useState } from 'react'
import { Button, Form, Segment } from 'semantic-ui-react'
import { Activity } from '../../../app/models/activity';
import { useStore } from '../../../app/stores/store';

interface Props {
activity: Activity | undefined;
closeForm: () => void;
createOrEdit: (activity: Activity) => void;
submitting: boolean;
}

export default function ActivityForm({ activity: selectedActivity, closeForm, createOrEdit, submitting }: Props) {
export default function ActivityForm({ createOrEdit, submitting }: Props) {
const { activityStore } = useStore();
const { selectedActivity, closeForm } = activityStore;

const initialState = selectedActivity ?? {
id: '',
Expand Down
7 changes: 5 additions & 2 deletions client-app/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import 'semantic-ui-css/semantic.min.css';
import './app/layout/styles.css';
import App from './app/layout/App';
import reportWebVitals from './reportWebVitals';
import { store, StoreContext } from './app/stores/store';

ReactDOM.render(
<App />,
document.getElementById('root')
<StoreContext.Provider value={store}>
<App />
</StoreContext.Provider>,
document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
Expand Down

0 comments on commit ad4ece5

Please sign in to comment.