Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Campaign Index and Create/Edit form #411

Merged
merged 35 commits into from
Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2c48b7b
feat: #384 add campaigntable
luigibesani Feb 18, 2022
9b96f02
feat: #384 add campaign data type
luigibesani Feb 18, 2022
5e4135e
chore: #384 adjust CampaignController to provide 10 rows
luigibesani Feb 18, 2022
9612aa1
feat: #386 add CampaignCreateForm and update exports
luigibesani Feb 18, 2022
c3e6e1b
feat: #384 add campaign index page to applayout
luigibesani Feb 18, 2022
4961046
feat: #386 add campaign create page
luigibesani Feb 18, 2022
836081c
fix: #384 underline campaign title properly added
luigibesani Feb 18, 2022
4ffbc5a
fix: #386 adjust campaigncreateform to make use of create and save bu…
luigibesani Feb 18, 2022
270e10f
feat: #384 add join button to campaigntable component
luigibesani Feb 18, 2022
ba13c07
chore: #384 adjust campaign controller to load characters
luigibesani Feb 19, 2022
8b7536b
feat: #384 add character column to campaigntable
luigibesani Feb 19, 2022
9a2049f
feat: #386 add CampaignCreate page and adjust campaigncreateform
luigibesani Feb 19, 2022
12cc77b
fix: #386 change adventure_id to adventure.id for campaignstorereques…
luigibesani Feb 19, 2022
5d445e6
fix: #384 adjust store function to load characters properly
luigibesani Feb 19, 2022
97e049b
fix: #386 remove code field from campaigncreateform
luigibesani Feb 19, 2022
d9b923d
fix: #384 remove code fields from form prop type
luigibesani Feb 19, 2022
4407870
fix: #384 specify adventure_id in data
m-triassi Feb 20, 2022
1682a9d
chore: #384 fix comment typo
m-triassi Feb 20, 2022
9dc3573
test: #384 expect new adventure id format in test
m-triassi Feb 20, 2022
cf20374
feat: #384 add formatter to properly display character name
luigibesani Feb 20, 2022
e7983c5
chore: #384 #386 update campaign data type and add mock data
luigibesani Feb 20, 2022
d0adbb4
test: #384 add index test and table test
luigibesani Feb 20, 2022
a4b8821
test: #386 add create form tests
luigibesani Feb 20, 2022
9902b6a
test: #386 append to campaign create form tests
luigibesani Feb 20, 2022
c894e5f
chore: #386 adjust CampaignController display autocomplete adventure …
luigibesani Feb 20, 2022
f5b2e68
chore: #384 #386 address requested changes
luigibesani Feb 20, 2022
530a548
fix: #386 adjust CampaingController to pass down adventure entity for…
luigibesani Feb 20, 2022
f5307df
fix: #384 remove console log line
luigibesani Feb 20, 2022
3b1f848
fix: #384 adjust CampaignTable to allow for deletion of rows
luigibesani Feb 20, 2022
31cd68e
refactor: #384 merge itemFormatter and characterNameFormatter methods…
luigibesani Feb 20, 2022
f0df0ed
fix: #384 #386 update php unit test to expect inertia link
luigibesani Feb 20, 2022
e6716ae
Merge branch 'feat-campaign-management' of github.com:DnD-Montreal/se…
luigibesani Feb 21, 2022
3a55a53
fix: #386 adjust index_displays_view to assert inertia route
luigibesani Feb 21, 2022
417fa4b
fix: #386 add Inertia testing assert namespace
luigibesani Feb 21, 2022
3ea20a5
test: #386 fix failing jest test
luigibesani Feb 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions app/Http/Controllers/CampaignController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Models\Campaign;
use App\Models\User;
use App\Models\Character;
use App\Models\Adventure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
Expand All @@ -19,18 +20,30 @@ class CampaignController extends Controller
*/
public function index(Request $request)
{
$campaigns = Auth::user()->campaigns()->get();

return view('campaign.index', compact('campaigns'));
$characters = Character::where('user_id', Auth::user()->id)->get();
$campaigns = Auth::user()
->campaigns()
->get();
$campaigns->load('characters')->where('user_id', Auth::user()->id);
return Inertia::render('Campaign/Campaign', compact('campaigns', 'characters'));
}

/**
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Inertia\Response
*/
public function create(Request $request)
{
return view('campaign.create');
$data = $request->validate([
'search' => 'sometimes|string',
]);
$search = $data['search'] ?? '';
$adventures = Adventure::filtered($search)->get(['id', 'title', 'code']);
$characters = Character::where('user_id', Auth::user()->id)->get();
return Inertia::render(
'Campaign/Create/CampaignCreate',
compact('characters', 'adventures')
);
}

/**
Expand All @@ -40,10 +53,13 @@ public function create(Request $request)
public function store(CampaignStoreRequest $request)
{
$data = $request->validated();
// TODO: Look into using prepareForValidation in Request Validator
$data['adventure_id'] = $data['adventure']['id'];
unset($data['adventure']['id']);

$campaign = Campaign::create($data);

//user joins capaign
//user joins campaign
$user = Auth::user();

if ($request->has('character_id')) {
Expand Down
4 changes: 2 additions & 2 deletions app/Http/Requests/CampaignStoreRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ public function authorize()
public function rules()
{
return [
'adventure_id' => ['required', 'integer', 'exists:adventures,id'],
'adventure.id' => ['required', 'integer', 'exists:adventures,id'],
'title' => ['required', 'string'],
'character_id' => ['sometimes','exists:characters,id']
'character_id' => ['sometimes', 'exists:characters,id'],
];
}
}
66 changes: 66 additions & 0 deletions resources/js/Components/Form/CampaignCreateForm.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {fireEvent, render, screen} from '@testing-library/react'
import {characterData} from 'Mock/character-data'
import React from 'react'

import CampaignCreateForm from './CampaignCreateForm'

const mockFunction = jest.fn()

const editProps = {
type: 'Edit',
onCloserDrawer: mockFunction,
editData: {
title: 'Campaign1',
character_id: 1,
adventure: 0,
},
editId: 0,
characters: characterData,
adventures: [],
}

const createProps = {
type: 'Create',
adventures: [
{id: 1, title: 'adventure1'},
{id: 2, title: 'adventure2'},
],
characters: [
{id: 1, name: 'John'},
{id: 2, name: 'John2'},
],
}

describe('<CampaignCreateForm />', () => {
it('edit component should render and close', () => {
const component = render(<CampaignCreateForm {...editProps} />)
expect(component).toBeDefined()
fireEvent.click(screen.getByText('Cancel'))
})
it('create component should render', () => {
const component = render(<CampaignCreateForm {...createProps} />)
expect(component).toBeDefined()
})
it('create component fields test', () => {
render(<CampaignCreateForm {...createProps} />)
const adventureInputField = document.querySelector('#adventures')
const titleField = document.querySelector('#title')
const characterField = document.querySelector('input[name="Assigned Character"]')

fireEvent.change(adventureInputField, {target: {value: 2}})
fireEvent.change(titleField, {target: {value: '123'}})
fireEvent.change(characterField, {target: {value: createProps.characters[1]}})

fireEvent.click(screen.getByText('Create'))
})
it('edit component fields test', () => {
render(<CampaignCreateForm {...editProps} />)
const adventureInputField = document.querySelector('#adventures')
const titleField = document.querySelector('#title')
const characterField = document.querySelector('input[name="Assigned Character"]')
fireEvent.change(adventureInputField, {target: {value: 2}})
fireEvent.change(titleField, {target: {value: '123'}})
fireEvent.change(characterField, {target: {value: createProps.characters[1]}})
fireEvent.click(screen.getByText('Save'))
})
})
141 changes: 141 additions & 0 deletions resources/js/Components/Form/CampaignCreateForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import {useForm} from '@inertiajs/inertia-react'
import {Button, Grid, TextField, Typography} from '@mui/material'
import {Autocomplete, ErrorText, Select} from 'Components'
import React from 'react'
import styled from 'styled-components'
import {adventureType} from 'Types/adventure-data'
import {CampaignData} from 'Types/campaign-data'
import {CharacterData} from 'Types/character-data'
import route from 'ziggy-js'

type CampaignCreateFormPropType = {
type: 'Edit' | 'Create'
onCloseDrawer?: () => void
editData?: CampaignData
editId?: number
adventures: adventureType[]
characters: CharacterData[]
}

type CampaignFormDataType = {
title: string | undefined
character_id: number | null
adventure: adventureType | undefined
[key: string]: any
}

const StyledGrid = styled(Grid)`
margin-bottom: 16px;
`

const CampaignCreateForm = ({
type,
onCloseDrawer,
editData,
editId = 0,
adventures,
characters,
}: CampaignCreateFormPropType) => {
const CAMPAIGN_CREATE_FORM_INITIAL_VALUE: CampaignFormDataType = {
title: '',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use undefined instead

character_id: null,
adventure: undefined,
}
const CAMPAIGN_INITIAL_VALUE: CampaignFormDataType =
type === 'Create'
? CAMPAIGN_CREATE_FORM_INITIAL_VALUE
: {
title: editData?.title,
character_id: editData?.character_id || null,
adventure: editData?.adventure || undefined,
}

const {data, setData, errors, put, post} =
useForm<CampaignFormDataType>(CAMPAIGN_INITIAL_VALUE)

return (
<>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont need this empty tag

<Grid justifyContent='flex-start' container spacing={2}>
<Grid item xs={12}>
<Typography>
Fill out the following fields with your campaign details.
</Typography>
</Grid>
<StyledGrid item xs={12} md={type === 'Edit' ? 12 : 5}>
<TextField
fullWidth
id='title'
label='Title'
name='Title'
value={data.title}
onChange={(e) => setData('title', e.target.value)}
/>
{errors?.title && <ErrorText message={errors?.title} />}
</StyledGrid>
{type === 'Create' && <StyledGrid item md={2} />}
<StyledGrid item xs={12} md={type === 'Edit' ? 12 : 5}>
<Select
id='character_id'
label='Assigned Character'
name='Assigned Character'
value={data.character_id}
onChange={(e) =>
setData('character_id', parseInt(e.target.value))
}
options={characters}
/>
{errors?.character_id && <ErrorText message={errors?.character_id} />}
</StyledGrid>
<StyledGrid item xs={12} md={type === 'Edit' ? 12 : 5}>
<Autocomplete
label='Adventure'
id='adventures'
fieldKey='adventures'
onChange={(_, value) => setData('adventure', value)}
defaultValue={data.adventure}
getOptionLabel={(option) => `${option.code} - ${option.title}`}
options={adventures}
resetUrl={
type === 'Create'
? route('campaign.create')
: route('campaign.index')
}
/>
{errors['adventure.id'] && (
<ErrorText message={errors['adventure.id']} />
)}
</StyledGrid>
</Grid>
<Grid container spacing={4}>
<Grid item xs={4}>
<Button onClick={() => onCloseDrawer && onCloseDrawer()} fullWidth>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont think this covers when you cancel from create view

Cancel
</Button>
</Grid>
<Grid item xs={4} />
<Grid item xs={4}>
<Button
variant='contained'
fullWidth
onClick={() => {
if (type === 'Create') {
post(route('campaign.store'))
}
if (type === 'Edit') {
put(route('campaign.update', [editId]))
if (onCloseDrawer) {
onCloseDrawer()
}
}
}}>
{type === 'Create' ? 'Create' : 'Save'}
</Button>
</Grid>
{type === 'Create' && <StyledGrid item md={2} />}
</Grid>
</>
)
}

CampaignCreateForm.displayName = 'CampaignCreateForm'
export default CampaignCreateForm
1 change: 1 addition & 0 deletions resources/js/Components/Form/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export {default as BulkEntryCreateForm} from './BulkEntryCreateForm'
export {default as CampaignCreateForm} from './CampaignCreateForm'
export {default as CharacterCreateForm} from './CharacterCreateForm'
export {default as CharacterImportForm} from './CharacterImportForm'
export {default as DmEntryCreateForm} from './DmEntryCreateForm'
Expand Down
35 changes: 35 additions & 0 deletions resources/js/Components/Table/CampaignTable/CampaignTable.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {fireEvent, render, screen} from '@testing-library/react'
import {campaignData} from 'Mock/campaign-data'
import React from 'react'

import CampaignTable from './CampaignTable'

const mockFunction = jest.fn()
const props = {
data: campaignData,
setEditData: mockFunction,
setEditId: mockFunction,
setIsEditDrawerOpen: mockFunction,
}

describe('CampaignTable', () => {
it('Component should render', () => {
const component = render(<CampaignTable {...props} />)
expect(component).toBeDefined()
})
it('Pagination handleChangePage should work', () => {
render(<CampaignTable {...props} />)
fireEvent.click(screen.getByTitle('Go to next page'))
})
it('Pagination 5 click should work', () => {
render(<CampaignTable {...props} />)
fireEvent.click(screen.getByLabelText('10'))
fireEvent.click(screen.getByDisplayValue('10'))
})
it('Edit Icon', () => {
render(<CampaignTable {...props} />)
const editIcon = document.querySelector('[data-testid="EditIcon"]')
fireEvent.click(editIcon)
expect(mockFunction).toBeCalled()
})
})
Loading