Upload custom icon on client
This commit is contained in:
parent
6c067bee31
commit
12974ab01b
6 changed files with 81 additions and 29 deletions
|
@ -10,7 +10,7 @@ RUN npm install --production
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN mkdir -p ./public ./data \
|
RUN mkdir -p ./public ./data ./data/uploads \
|
||||||
&& cd ./client \
|
&& cd ./client \
|
||||||
&& npm install --production \
|
&& npm install --production \
|
||||||
&& npm run build \
|
&& npm run build \
|
||||||
|
|
|
@ -11,7 +11,7 @@ RUN apk --no-cache --virtual build-dependencies add python make g++ \
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN mkdir -p ./public ./data \
|
RUN mkdir -p ./public ./data ./data/uploads \
|
||||||
&& cd ./client \
|
&& cd ./client \
|
||||||
&& npm install --production \
|
&& npm install --production \
|
||||||
&& npm run build \
|
&& npm run build \
|
||||||
|
|
7
client/src/components/Apps/AppForm/AppForm.module.css
Normal file
7
client/src/components/Apps/AppForm/AppForm.module.css
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.Switch {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Switch:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
|
@ -3,18 +3,23 @@ import { connect } from 'react-redux';
|
||||||
import { addApp, updateApp } from '../../../store/actions';
|
import { addApp, updateApp } from '../../../store/actions';
|
||||||
import { App, NewApp } from '../../../interfaces';
|
import { App, NewApp } from '../../../interfaces';
|
||||||
|
|
||||||
|
import classes from './AppForm.module.css';
|
||||||
|
|
||||||
import ModalForm from '../../UI/Forms/ModalForm/ModalForm';
|
import ModalForm from '../../UI/Forms/ModalForm/ModalForm';
|
||||||
import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
|
import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
|
||||||
import Button from '../../UI/Buttons/Button/Button';
|
import Button from '../../UI/Buttons/Button/Button';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
modalHandler: () => void;
|
modalHandler: () => void;
|
||||||
addApp: (formData: NewApp) => any;
|
addApp: (formData: NewApp | FormData) => any;
|
||||||
updateApp: (id: number, formData: NewApp) => any;
|
updateApp: (id: number, formData: NewApp) => any;
|
||||||
app?: App;
|
app?: App;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppForm = (props: ComponentProps): JSX.Element => {
|
const AppForm = (props: ComponentProps): JSX.Element => {
|
||||||
|
const [useCustomIcon, toggleUseCustomIcon] = useState<boolean>(true);
|
||||||
|
const [customIcon, setCustomIcon] = useState<File | null>(null);
|
||||||
const [formData, setFormData] = useState<NewApp>({
|
const [formData, setFormData] = useState<NewApp>({
|
||||||
name: '',
|
name: '',
|
||||||
url: '',
|
url: '',
|
||||||
|
@ -52,11 +57,27 @@ const AppForm = (props: ComponentProps): JSX.Element => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fileChangeHandler = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
if (e.target.files) {
|
||||||
|
setCustomIcon(e.target.files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const formSubmitHandler = (e: SyntheticEvent<HTMLFormElement>): void => {
|
const formSubmitHandler = (e: SyntheticEvent<HTMLFormElement>): void => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (!props.app) {
|
if (!props.app) {
|
||||||
props.addApp(formData);
|
if (customIcon) {
|
||||||
|
const data = new FormData();
|
||||||
|
data.append('icon', customIcon);
|
||||||
|
|
||||||
|
data.append('name', formData.name);
|
||||||
|
data.append('url', formData.url);
|
||||||
|
|
||||||
|
props.addApp(data);
|
||||||
|
} else {
|
||||||
|
props.addApp(formData);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
props.updateApp(props.app.id, formData);
|
props.updateApp(props.app.id, formData);
|
||||||
props.modalHandler();
|
props.modalHandler();
|
||||||
|
@ -108,26 +129,51 @@ const AppForm = (props: ComponentProps): JSX.Element => {
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<InputGroup>
|
{!useCustomIcon
|
||||||
<label htmlFor='icon'>App Icon</label>
|
// use mdi icon
|
||||||
<input
|
? (<InputGroup>
|
||||||
type='text'
|
<label htmlFor='icon'>App Icon</label>
|
||||||
name='icon'
|
<input
|
||||||
id='icon'
|
type='text'
|
||||||
placeholder='book-open-outline'
|
name='icon'
|
||||||
required
|
id='icon'
|
||||||
value={formData.icon}
|
placeholder='book-open-outline'
|
||||||
onChange={(e) => inputChangeHandler(e)}
|
required
|
||||||
/>
|
value={formData.icon}
|
||||||
<span>
|
onChange={(e) => inputChangeHandler(e)}
|
||||||
Use icon name from MDI.
|
/>
|
||||||
<a
|
<span>
|
||||||
href='https://materialdesignicons.com/'
|
Use icon name from MDI.
|
||||||
target='blank'>
|
<a
|
||||||
{' '}Click here for reference
|
href='https://materialdesignicons.com/'
|
||||||
</a>
|
target='blank'>
|
||||||
</span>
|
{' '}Click here for reference
|
||||||
</InputGroup>
|
</a>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
onClick={() => toggleUseCustomIcon(!useCustomIcon)}
|
||||||
|
className={classes.Switch}>
|
||||||
|
Switch to custom icon upload
|
||||||
|
</span>
|
||||||
|
</InputGroup>)
|
||||||
|
// upload custom icon
|
||||||
|
: (<InputGroup>
|
||||||
|
<label htmlFor='icon'>App Icon</label>
|
||||||
|
<input
|
||||||
|
type='file'
|
||||||
|
name='icon'
|
||||||
|
id='icon'
|
||||||
|
required
|
||||||
|
onChange={(e) => fileChangeHandler(e)}
|
||||||
|
accept='.jpg,.jpeg,.png'
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
onClick={() => toggleUseCustomIcon(!useCustomIcon)}
|
||||||
|
className={classes.Switch}>
|
||||||
|
Switch to MDI
|
||||||
|
</span>
|
||||||
|
</InputGroup>)
|
||||||
|
}
|
||||||
{!props.app
|
{!props.app
|
||||||
? <Button>Add new application</Button>
|
? <Button>Add new application</Button>
|
||||||
: <Button>Update application</Button>
|
: <Button>Update application</Button>
|
||||||
|
|
|
@ -61,7 +61,7 @@ export interface AddAppAction {
|
||||||
payload: App;
|
payload: App;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addApp = (formData: NewApp) => async (dispatch: Dispatch) => {
|
export const addApp = (formData: NewApp | FormData) => async (dispatch: Dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await axios.post<ApiResponse<App>>('/api/apps', formData);
|
const res = await axios.post<ApiResponse<App>>('/api/apps', formData);
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ export const addApp = (formData: NewApp) => async (dispatch: Dispatch) => {
|
||||||
type: ActionTypes.createNotification,
|
type: ActionTypes.createNotification,
|
||||||
payload: {
|
payload: {
|
||||||
title: 'Success',
|
title: 'Success',
|
||||||
message: `App ${formData.name} added`
|
message: `App added`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ export interface UpdateAppAction {
|
||||||
payload: App;
|
payload: App;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateApp = (id: number, formData: NewApp) => async (dispatch: Dispatch) => {
|
export const updateApp = (id: number, formData: NewApp | FormData) => async (dispatch: Dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await axios.put<ApiResponse<App>>(`/api/apps/${id}`, formData);
|
const res = await axios.put<ApiResponse<App>>(`/api/apps/${id}`, formData);
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ export const updateApp = (id: number, formData: NewApp) => async (dispatch: Disp
|
||||||
type: ActionTypes.createNotification,
|
type: ActionTypes.createNotification,
|
||||||
payload: {
|
payload: {
|
||||||
title: 'Success',
|
title: 'Success',
|
||||||
message: `App ${formData.name} updated`
|
message: `App updated`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ exports.createApp = asyncWrapper(async (req, res, next) => {
|
||||||
let _body = { ...req.body };
|
let _body = { ...req.body };
|
||||||
|
|
||||||
if (req.file) {
|
if (req.file) {
|
||||||
console.log(req.file.filename)
|
|
||||||
_body.icon = req.file.filename;
|
_body.icon = req.file.filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue