Skip to content
Snippets Groups Projects
Commit 8426bb18 authored by robban64's avatar robban64
Browse files

Merge branch 'dev' of...

Merge branch 'dev' of https://gitlab.liu.se/tddd96-grupp11/teknikattan-scoring-system into 115-restructure-api-routes
parents ffb2f8bb 4a1807e2
No related branches found
No related tags found
2 merge requests!92Resolve "Refactor/minimize editor code",!88Resolve "Restructure api routes"
Pipeline #41841 passed
This commit is part of merge request !92. Comments created here will be created in the context of that merge request.
import { Editor } from '@tinymce/tinymce-react'
import axios from 'axios'
import React, { useState } from 'react'
import { Rnd } from 'react-rnd'
import { ComponentTypes } from '../../../enum/ComponentTypes'
import { useAppSelector } from '../../../hooks'
import { TextComponent } from '../../../interfaces/ApiModels'
import { Component, ImageComponent, TextComponent } from '../../../interfaces/ApiModels'
import { Position, Size } from '../../../interfaces/Components'
import CheckboxComponent from './CheckboxComponent'
import ImageComponentDisplay from './ImageComponentDisplay'
import { TextComponentContainer } from './styled'
type ImageComponentProps = {
component: TextComponent
component: Component
}
const TextComponentDisplay = ({ component }: ImageComponentProps) => {
const RndComponent = ({ component }: ImageComponentProps) => {
const [hover, setHover] = useState(false)
const [currentPos, setCurrentPos] = useState<Position>({ x: component.x, y: component.y })
const [currentSize, setCurrentSize] = useState<Size>({ w: component.w, h: component.h })
const competitionId = useAppSelector((state) => state.editor.competition.id)
const slideId = useAppSelector((state) => state.editor.activeSlideId)
if (component.id === 1) console.log(component)
const handleEditorChange = (e: any) => {
console.log('Content was updated:', e.target.getContent())
axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
data: { ...component.data, text: e.target.getContent() },
})
}
const handleUpdatePos = (pos: Position) => {
axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
// TODO: change path to /slides/${slideId}
axios.put(`/competitions/${competitionId}/slides/0/components/${component.id}`, {
x: pos.x,
y: pos.y,
})
}
const handleUpdateSize = () => {
axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
w: currentSize.w,
h: currentSize.h,
const handleUpdateSize = (size: Size) => {
// TODO: change path to /slides/${slideId}
axios.put(`/competitions/${competitionId}/slides/0/components/${component.id}`, {
w: size.w,
h: size.h,
})
}
const renderInnerComponent = () => {
switch (component.type_id) {
case ComponentTypes.Checkbox:
return <CheckboxComponent key={component.id} component={component} />
case ComponentTypes.Text:
return (
<TextComponentContainer
hover={hover}
dangerouslySetInnerHTML={{ __html: (component as TextComponent).data.text }}
/>
)
case ComponentTypes.Image:
return <ImageComponentDisplay key={component.id} component={component as ImageComponent} />
default:
break
}
}
return (
<Rnd
minWidth={50}
......@@ -43,38 +61,29 @@ const TextComponentDisplay = ({ component }: ImageComponentProps) => {
setCurrentPos({ x: d.x, y: d.y })
handleUpdatePos(d)
}}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
size={{ width: currentSize.w, height: currentSize.h }}
position={{ x: currentPos.x, y: currentPos.y }}
onResize={(e, direction, ref, delta, position) => {
onResizeStop={(e, direction, ref, delta, position) => {
setCurrentSize({
w: ref.offsetWidth,
h: ref.offsetHeight,
})
setCurrentPos(position)
handleUpdateSize({ w: ref.offsetWidth, h: ref.offsetHeight })
handleUpdatePos(position)
}}
onResizeStop={handleUpdateSize}
onResize={(e, direction, ref, delta, position) =>
setCurrentSize({
w: ref.offsetWidth,
h: ref.offsetHeight,
})
}
>
<div style={{ height: '100%', width: '100%' }}>
<Editor
initialValue={component.data.text}
init={{
height: '100%',
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount',
],
toolbar:
'undo redo | formatselect | fontselect | bold italic backcolor | \
alignleft aligncenter alignright alignjustify | \
bullist numlist outdent indent | removeformat | help',
}}
onChange={handleEditorChange}
/>
</div>
{renderInnerComponent()}
</Rnd>
)
}
export default TextComponentDisplay
export default RndComponent
import React from 'react'
import { ComponentTypes } from '../../../enum/ComponentTypes'
import { useAppSelector } from '../../../hooks'
import { ImageComponent, TextComponent } from '../../../interfaces/ApiModels'
import CheckboxComponent from './CheckboxComponent'
import ImageComponentDisplay from './ImageComponentDisplay'
import RndComponent from './RndComponent'
import { SlideEditorContainer, SlideEditorContainerRatio, SlideEditorPaper } from './styled'
import TextComponentDisplay from './TextComponentDisplay'
const SlideEditor: React.FC = () => {
const components = useAppSelector(
......@@ -16,19 +12,7 @@ const SlideEditor: React.FC = () => {
<SlideEditorContainer>
<SlideEditorContainerRatio>
<SlideEditorPaper>
{components &&
components.map((component) => {
switch (component.type_id) {
case ComponentTypes.Checkbox:
return <CheckboxComponent key={component.id} component={component} />
case ComponentTypes.Text:
return <TextComponentDisplay key={component.id} component={component as TextComponent} />
case ComponentTypes.Image:
return <ImageComponentDisplay key={component.id} component={component as ImageComponent} />
default:
break
}
})}
{components && components.map((component) => <RndComponent key={component.id} component={component} />)}
</SlideEditorPaper>
</SlideEditorContainerRatio>
</SlideEditorContainer>
......
......@@ -6,6 +6,7 @@ import {
DialogContent,
DialogContentText,
DialogTitle,
Divider,
FormControl,
InputLabel,
List,
......@@ -26,7 +27,8 @@ import { useParams } from 'react-router-dom'
import { getEditorCompetition } from '../../../actions/editor'
import { useAppDispatch, useAppSelector } from '../../../hooks'
import { QuestionAlternative, TextComponent } from '../../../interfaces/ApiModels'
import { HiddenInput } from './styled'
import { HiddenInput, TextCard } from './styled'
import TextComponentEdit from './TextComponentEdit'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
......@@ -234,9 +236,15 @@ const SlideSettings: React.FC = () => {
}
const handleAddText = async () => {
console.log('Add text component')
// TODO: post the new text]
// setTexts([...texts, { id: 'newText', name: 'New Text' }])
if (activeSlide) {
await axios.post(`/competitions/${id}/slides/${activeSlide?.order}/components`, {
type_id: 1,
data: { text: 'Ny text' },
w: 315,
h: 50,
})
dispatch(getEditorCompetition(id))
}
}
const GreenCheckbox = withStyles({
......@@ -338,9 +346,8 @@ const SlideSettings: React.FC = () => {
helperText="Lämna blank för att inte använda timerfunktionen"
label="Timer"
type="number"
defaultValue={activeSlide?.timer || 0}
onChange={updateTimer}
value={timer}
value={timer || ''}
/>
</ListItem>
......@@ -383,13 +390,13 @@ const SlideSettings: React.FC = () => {
</ListItem>
{texts &&
texts.map((text) => (
<div key={text.id}>
<ListItem divider>
<TextField className={classes.textInput} label={text.data.text} variant="outlined" />
<CloseIcon className={classes.clickableIcon} />
</ListItem>
</div>
<TextCard elevation={4} key={text.id}>
<TextComponentEdit component={text} />
<Divider />
</TextCard>
))}
<ListItem className={classes.center} button onClick={handleAddText}>
<Typography className={classes.addButtons} variant="button">
Lägg till text
......
import { Editor } from '@tinymce/tinymce-react'
import { mount } from 'enzyme'
import React from 'react'
import { Provider } from 'react-redux'
import store from '../../../store'
import TextComponentDisplay from './TextComponentDisplay'
it('renders text component display', () => {
const testText = 'TEST'
const container = mount(
<Provider store={store}>
<TextComponentDisplay
component={{ id: 0, x: 0, y: 0, w: 0, h: 0, data: { text: testText, font: '123123' }, type_id: 2 }}
/>
</Provider>
)
expect(container.find(Editor).prop('initialValue')).toBe(testText)
})
import { Editor } from '@tinymce/tinymce-react'
import axios from 'axios'
import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import { getEditorCompetition } from '../../../actions/editor'
import { useAppDispatch, useAppSelector } from '../../../hooks'
import { TextComponent } from '../../../interfaces/ApiModels'
import { DeleteTextButton } from './styled'
type ImageComponentProps = {
component: TextComponent
}
interface CompetitionParams {
id: string
}
const TextComponentEdit = ({ component }: ImageComponentProps) => {
const { id }: CompetitionParams = useParams()
const competitionId = useAppSelector((state) => state.editor.competition.id)
const [content, setContent] = useState('')
const [timerHandle, setTimerHandle] = React.useState<number | undefined>(undefined)
const dispatch = useAppDispatch()
useEffect(() => {
setContent(component.data.text)
}, [])
const handleSaveText = async (a: string) => {
setContent(a)
if (timerHandle) {
clearTimeout(timerHandle)
setTimerHandle(undefined)
}
//Only updates 250ms after last input was made to not spam
setTimerHandle(
window.setTimeout(async () => {
console.log('Content was updated on server. id: ', component.id)
await axios.put(`/competitions/${competitionId}/slides/0/components/${component.id}`, {
data: { ...component.data, text: a },
})
dispatch(getEditorCompetition(id))
}, 250)
)
}
const handleDeleteText = async (componentId: number) => {
await axios.delete(`/competitions/${id}/slides/0/components/${componentId}`)
dispatch(getEditorCompetition(id))
}
return (
<div style={{ minHeight: '300px', height: '100%', width: '100%' }}>
<Editor
value={content || ''}
init={{
height: '300px',
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount',
],
toolbar:
'undo redo save | fontselect | formatselect | bold italic backcolor | \
alignleft aligncenter alignright alignjustify | \
bullist numlist outdent indent | removeformat | help',
}}
onEditorChange={(a, e) => handleSaveText(a)}
/>
<DeleteTextButton variant="contained" color="secondary" onClick={() => handleDeleteText(component.id)}>
Ta bort
</DeleteTextButton>
</div>
)
}
export default TextComponentEdit
import { Tab } from '@material-ui/core'
import { Button, Card, Tab } from '@material-ui/core'
import styled from 'styled-components'
export const SettingsTab = styled(Tab)`
......@@ -44,3 +44,23 @@ export const ToolbarPadding = styled.div`
height: 0;
padding-top: 55px;
`
export const TextCard = styled(Card)`
margin-bottom: 15px;
margin-top: 10px;
`
export const DeleteTextButton = styled(Button)`
width: 100%;
margin-bottom: 7px;
`
interface TextComponentContainerProps {
hover: boolean
}
export const TextComponentContainer = styled.div<TextComponentContainerProps>`
height: 100%;
width: 100%;
border: solid ${(props) => (props.hover ? 1 : 0)}px;
`
......@@ -67,7 +67,7 @@ const PresenterViewPage: React.FC = () => {
socket_connect()
socketSetSlide // Behövs denna?
setTimeout(startCompetition, 500) // Ghetto, wait for everything to load
console.log(id)
// console.log(id)
}, [])
const handleOpenPopover = (event: React.MouseEvent<HTMLButtonElement>) => {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment