mirror of
https://github.com/pawelmalak/flame.git
synced 2025-07-19 03:29:37 +02:00
Set app visibility
This commit is contained in:
parent
ee9aefa4fa
commit
d1738a0a3e
7 changed files with 127 additions and 105 deletions
|
@ -8,6 +8,7 @@ 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 { inputHandler, newAppTemplate } from '../../../utility';
|
||||||
|
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
modalHandler: () => void;
|
modalHandler: () => void;
|
||||||
|
@ -19,32 +20,27 @@ interface ComponentProps {
|
||||||
const AppForm = (props: ComponentProps): JSX.Element => {
|
const AppForm = (props: ComponentProps): JSX.Element => {
|
||||||
const [useCustomIcon, toggleUseCustomIcon] = useState<boolean>(false);
|
const [useCustomIcon, toggleUseCustomIcon] = useState<boolean>(false);
|
||||||
const [customIcon, setCustomIcon] = useState<File | null>(null);
|
const [customIcon, setCustomIcon] = useState<File | null>(null);
|
||||||
const [formData, setFormData] = useState<NewApp>({
|
const [formData, setFormData] = useState<NewApp>(newAppTemplate);
|
||||||
name: '',
|
|
||||||
url: '',
|
|
||||||
icon: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.app) {
|
if (props.app) {
|
||||||
setFormData({
|
setFormData({
|
||||||
name: props.app.name,
|
...props.app,
|
||||||
url: props.app.url,
|
|
||||||
icon: props.app.icon,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setFormData({
|
setFormData(newAppTemplate);
|
||||||
name: '',
|
|
||||||
url: '',
|
|
||||||
icon: '',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [props.app]);
|
}, [props.app]);
|
||||||
|
|
||||||
const inputChangeHandler = (e: ChangeEvent<HTMLInputElement>): void => {
|
const inputChangeHandler = (
|
||||||
setFormData({
|
e: ChangeEvent<HTMLInputElement | HTMLSelectElement>,
|
||||||
...formData,
|
options?: { isNumber?: boolean; isBool?: boolean }
|
||||||
[e.target.name]: e.target.value,
|
) => {
|
||||||
|
inputHandler<NewApp>({
|
||||||
|
e,
|
||||||
|
options,
|
||||||
|
setStateHandler: setFormData,
|
||||||
|
state: formData,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,11 +82,7 @@ const AppForm = (props: ComponentProps): JSX.Element => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setFormData({
|
setFormData(newAppTemplate);
|
||||||
name: '',
|
|
||||||
url: '',
|
|
||||||
icon: '',
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -98,6 +90,7 @@ const AppForm = (props: ComponentProps): JSX.Element => {
|
||||||
modalHandler={props.modalHandler}
|
modalHandler={props.modalHandler}
|
||||||
formHandler={formSubmitHandler}
|
formHandler={formSubmitHandler}
|
||||||
>
|
>
|
||||||
|
{/* NAME */}
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor="name">App Name</label>
|
<label htmlFor="name">App Name</label>
|
||||||
<input
|
<input
|
||||||
|
@ -110,6 +103,8 @@ const AppForm = (props: ComponentProps): JSX.Element => {
|
||||||
onChange={(e) => inputChangeHandler(e)}
|
onChange={(e) => inputChangeHandler(e)}
|
||||||
/>
|
/>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
|
{/* URL */}
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<label htmlFor="url">App URL</label>
|
<label htmlFor="url">App URL</label>
|
||||||
<input
|
<input
|
||||||
|
@ -121,17 +116,9 @@ const AppForm = (props: ComponentProps): JSX.Element => {
|
||||||
value={formData.url}
|
value={formData.url}
|
||||||
onChange={(e) => inputChangeHandler(e)}
|
onChange={(e) => inputChangeHandler(e)}
|
||||||
/>
|
/>
|
||||||
<span>
|
|
||||||
<a
|
|
||||||
href="https://github.com/pawelmalak/flame#supported-url-formats-for-applications-and-bookmarks"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
Check supported URL formats
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
|
{/* ICON */}
|
||||||
{!useCustomIcon ? (
|
{!useCustomIcon ? (
|
||||||
// use mdi icon
|
// use mdi icon
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
|
@ -182,6 +169,21 @@ const AppForm = (props: ComponentProps): JSX.Element => {
|
||||||
</span>
|
</span>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* VISIBILITY */}
|
||||||
|
<InputGroup>
|
||||||
|
<label htmlFor="isPublic">App visibility</label>
|
||||||
|
<select
|
||||||
|
id="isPublic"
|
||||||
|
name="isPublic"
|
||||||
|
value={formData.isPublic ? 1 : 0}
|
||||||
|
onChange={(e) => inputChangeHandler(e, { isBool: true })}
|
||||||
|
>
|
||||||
|
<option value={1}>Visible (anyone can access it)</option>
|
||||||
|
<option value={0}>Hidden (authentication required)</option>
|
||||||
|
</select>
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
{!props.app ? (
|
{!props.app ? (
|
||||||
<Button>Add new application</Button>
|
<Button>Add new application</Button>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -114,7 +114,7 @@ const AppTable = (props: ComponentProps): JSX.Element => {
|
||||||
<Droppable droppableId="apps">
|
<Droppable droppableId="apps">
|
||||||
{(provided) => (
|
{(provided) => (
|
||||||
<Table
|
<Table
|
||||||
headers={['Name', 'URL', 'Icon', 'Actions']}
|
headers={['Name', 'URL', 'Icon', 'Visibility', 'Actions']}
|
||||||
innerRef={provided.innerRef}
|
innerRef={provided.innerRef}
|
||||||
>
|
>
|
||||||
{localApps.map((app: App, index): JSX.Element => {
|
{localApps.map((app: App, index): JSX.Element => {
|
||||||
|
@ -143,6 +143,9 @@ const AppTable = (props: ComponentProps): JSX.Element => {
|
||||||
<td style={{ width: '200px' }}>{app.name}</td>
|
<td style={{ width: '200px' }}>{app.name}</td>
|
||||||
<td style={{ width: '200px' }}>{app.url}</td>
|
<td style={{ width: '200px' }}>{app.url}</td>
|
||||||
<td style={{ width: '200px' }}>{app.icon}</td>
|
<td style={{ width: '200px' }}>{app.icon}</td>
|
||||||
|
<td style={{ width: '200px' }}>
|
||||||
|
{app.isPublic ? 'Visible' : 'Hidden'}
|
||||||
|
</td>
|
||||||
{!snapshot.isDragging && (
|
{!snapshot.isDragging && (
|
||||||
<td className={classes.TableActions}>
|
<td className={classes.TableActions}>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -23,6 +23,9 @@ import AppGrid from './AppGrid/AppGrid';
|
||||||
import AppForm from './AppForm/AppForm';
|
import AppForm from './AppForm/AppForm';
|
||||||
import AppTable from './AppTable/AppTable';
|
import AppTable from './AppTable/AppTable';
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import { appTemplate } from '../../utility';
|
||||||
|
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
getApps: Function;
|
getApps: Function;
|
||||||
apps: App[];
|
apps: App[];
|
||||||
|
@ -36,16 +39,7 @@ const Apps = (props: ComponentProps): JSX.Element => {
|
||||||
const [modalIsOpen, setModalIsOpen] = useState(false);
|
const [modalIsOpen, setModalIsOpen] = useState(false);
|
||||||
const [isInEdit, setIsInEdit] = useState(false);
|
const [isInEdit, setIsInEdit] = useState(false);
|
||||||
const [isInUpdate, setIsInUpdate] = useState(false);
|
const [isInUpdate, setIsInUpdate] = useState(false);
|
||||||
const [appInUpdate, setAppInUpdate] = useState<App>({
|
const [appInUpdate, setAppInUpdate] = useState<App>(appTemplate);
|
||||||
name: 'string',
|
|
||||||
url: 'string',
|
|
||||||
icon: 'string',
|
|
||||||
isPinned: false,
|
|
||||||
orderId: 0,
|
|
||||||
id: 0,
|
|
||||||
createdAt: new Date(),
|
|
||||||
updatedAt: new Date(),
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (apps.length === 0) {
|
if (apps.length === 0) {
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import { Model } from '.';
|
import { Model } from '.';
|
||||||
|
|
||||||
export interface App extends Model {
|
|
||||||
name: string;
|
|
||||||
url: string;
|
|
||||||
icon: string;
|
|
||||||
isPinned: boolean;
|
|
||||||
orderId: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NewApp {
|
export interface NewApp {
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
}
|
isPublic: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface App extends Model, NewApp {
|
||||||
|
orderId: number;
|
||||||
|
isPinned: boolean;
|
||||||
|
}
|
||||||
|
|
|
@ -11,108 +11,115 @@ export interface State {
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
loading: true,
|
loading: true,
|
||||||
apps: [],
|
apps: [],
|
||||||
errors: undefined
|
errors: undefined,
|
||||||
}
|
};
|
||||||
|
|
||||||
const getApps = (state: State, action: Action): State => {
|
const getApps = (state: State, action: Action): State => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
loading: true,
|
loading: true,
|
||||||
errors: undefined
|
errors: undefined,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const getAppsSuccess = (state: State, action: Action): State => {
|
const getAppsSuccess = (state: State, action: Action): State => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
loading: false,
|
loading: false,
|
||||||
apps: action.payload
|
apps: action.payload,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const getAppsError = (state: State, action: Action): State => {
|
const getAppsError = (state: State, action: Action): State => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
loading: false,
|
loading: false,
|
||||||
errors: action.payload
|
errors: action.payload,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const pinApp = (state: State, action: Action): State => {
|
const pinApp = (state: State, action: Action): State => {
|
||||||
const tmpApps = [...state.apps];
|
const tmpApps = [...state.apps];
|
||||||
const changedApp = tmpApps.find((app: App) => app.id === action.payload.id);
|
const changedApp = tmpApps.find((app: App) => app.id === action.payload.id);
|
||||||
|
|
||||||
if (changedApp) {
|
if (changedApp) {
|
||||||
changedApp.isPinned = action.payload.isPinned;
|
changedApp.isPinned = action.payload.isPinned;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
apps: tmpApps
|
apps: tmpApps,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const addAppSuccess = (state: State, action: Action): State => {
|
const addAppSuccess = (state: State, action: Action): State => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
apps: [...state.apps, action.payload]
|
apps: [...state.apps, action.payload],
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const deleteApp = (state: State, action: Action): State => {
|
const deleteApp = (state: State, action: Action): State => {
|
||||||
const tmpApps = [...state.apps].filter((app: App) => app.id !== action.payload);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
apps: tmpApps
|
apps: [...state.apps].filter((app: App) => app.id !== action.payload),
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const updateApp = (state: State, action: Action): State => {
|
const updateApp = (state: State, action: Action): State => {
|
||||||
const tmpApps = [...state.apps];
|
const appIdx = state.apps.findIndex((app) => app.id === action.payload.id);
|
||||||
const appInUpdate = tmpApps.find((app: App) => app.id === action.payload.id);
|
|
||||||
|
|
||||||
if (appInUpdate) {
|
|
||||||
appInUpdate.name = action.payload.name;
|
|
||||||
appInUpdate.url = action.payload.url;
|
|
||||||
appInUpdate.icon = action.payload.icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
apps: tmpApps
|
apps: [
|
||||||
}
|
...state.apps.slice(0, appIdx),
|
||||||
}
|
{
|
||||||
|
...action.payload,
|
||||||
|
},
|
||||||
|
...state.apps.slice(appIdx + 1),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const reorderApps = (state: State, action: Action): State => {
|
const reorderApps = (state: State, action: Action): State => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
apps: action.payload
|
apps: action.payload,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const sortApps = (state: State, action: Action): State => {
|
const sortApps = (state: State, action: Action): State => {
|
||||||
const sortedApps = sortData<App>(state.apps, action.payload);
|
const sortedApps = sortData<App>(state.apps, action.payload);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
apps: sortedApps
|
apps: sortedApps,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const appReducer = (state = initialState, action: Action) => {
|
const appReducer = (state = initialState, action: Action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.getApps: return getApps(state, action);
|
case ActionTypes.getApps:
|
||||||
case ActionTypes.getAppsSuccess: return getAppsSuccess(state, action);
|
return getApps(state, action);
|
||||||
case ActionTypes.getAppsError: return getAppsError(state, action);
|
case ActionTypes.getAppsSuccess:
|
||||||
case ActionTypes.pinApp: return pinApp(state, action);
|
return getAppsSuccess(state, action);
|
||||||
case ActionTypes.addAppSuccess: return addAppSuccess(state, action);
|
case ActionTypes.getAppsError:
|
||||||
case ActionTypes.deleteApp: return deleteApp(state, action);
|
return getAppsError(state, action);
|
||||||
case ActionTypes.updateApp: return updateApp(state, action);
|
case ActionTypes.pinApp:
|
||||||
case ActionTypes.reorderApps: return reorderApps(state, action);
|
return pinApp(state, action);
|
||||||
case ActionTypes.sortApps: return sortApps(state, action);
|
case ActionTypes.addAppSuccess:
|
||||||
default: return state;
|
return addAppSuccess(state, action);
|
||||||
|
case ActionTypes.deleteApp:
|
||||||
|
return deleteApp(state, action);
|
||||||
|
case ActionTypes.updateApp:
|
||||||
|
return updateApp(state, action);
|
||||||
|
case ActionTypes.reorderApps:
|
||||||
|
return reorderApps(state, action);
|
||||||
|
case ActionTypes.sortApps:
|
||||||
|
return sortApps(state, action);
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export default appReducer;
|
export default appReducer;
|
||||||
|
|
17
client/src/utility/templateObjects/appTemplate.ts
Normal file
17
client/src/utility/templateObjects/appTemplate.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { App, NewApp } from '../../interfaces';
|
||||||
|
|
||||||
|
export const newAppTemplate: NewApp = {
|
||||||
|
name: '',
|
||||||
|
url: '',
|
||||||
|
icon: '',
|
||||||
|
isPublic: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const appTemplate: App = {
|
||||||
|
...newAppTemplate,
|
||||||
|
isPinned: false,
|
||||||
|
orderId: 0,
|
||||||
|
id: 0,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
|
@ -1,2 +1,3 @@
|
||||||
export * from './configTemplate';
|
export * from './configTemplate';
|
||||||
export * from './settingsTemplate';
|
export * from './settingsTemplate';
|
||||||
|
export * from './appTemplate';
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue