-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
48c5ed8
commit 7f42ecd
Showing
7 changed files
with
489 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
const format = function(number, n, x, s, c) { | ||
var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\D' : '$') + ')', | ||
num = parseFloat(number).toFixed(Math.max(0, ~~n)); | ||
|
||
return (c ? num.replace('.', c) : num).replace(new RegExp(re, 'g'), '$&' + (s || ',')); | ||
}; | ||
|
||
const formatLv = function(number) { | ||
return format(number, 2, 3, ' ', ','); | ||
}; | ||
|
||
const formatPop = function(number) { | ||
return format(Math.floor(number), 0, 3, ' ', ','); | ||
} | ||
|
||
const pad = num => { | ||
var s = String(num); | ||
while (s.length < 2) {s = "0" + s;} | ||
return s; | ||
}; | ||
|
||
const month = monthStr => { | ||
switch(monthStr) { | ||
case 0: return 'януари'; break; | ||
case 1: return 'февруари'; break; | ||
case 2: return 'март'; break; | ||
case 3: return 'април'; break; | ||
case 4: return 'май'; break; | ||
case 5: return 'юни'; break; | ||
case 6: return 'юли'; break; | ||
case 7: return 'август'; break; | ||
case 8: return 'септември'; break; | ||
case 9: return 'октомври'; break; | ||
case 10: return 'ноември'; break; | ||
case 11: return 'декември'; break; | ||
} | ||
}; | ||
|
||
const formatDay = d => { | ||
if(d >= 10 && d <= 20) | ||
return d + '-ти'; | ||
|
||
switch(d%10) { | ||
case 1: return d + '-ви'; break; | ||
case 2: return d + '-ри'; break; | ||
case 7: case 8: return d + '-ми'; break; | ||
default: return d + '-ти'; break; | ||
} | ||
}; | ||
|
||
const formatDate = dateTime => { | ||
const date = new Date(dateTime); | ||
|
||
return formatDay(date.getDate()) + ' ' + | ||
month(date.getMonth()) + ' '+ | ||
date.getFullYear(); | ||
}; | ||
|
||
const formatTime = dateTime => { | ||
const date = new Date(dateTime); | ||
return pad(date.getHours()) + ':' + | ||
pad(date.getMinutes()); | ||
}; | ||
|
||
const formatDateTime = dateTime => { | ||
return formatTime(dateTime) + ' ' + formatDateShort(dateTime); | ||
|
||
}; | ||
|
||
const formatSecs = secs => { | ||
return pad(Math.floor(secs / 60)) + ':' + pad(secs % 60); | ||
}; | ||
|
||
const formatDateShort = dateTime => { | ||
const date = new Date(dateTime); | ||
|
||
return pad(date.getDate()) + '.' + | ||
pad(date.getMonth()+1) + '.' + | ||
pad(date.getFullYear()); | ||
} | ||
|
||
const checkPaths = (path1, path2) => { | ||
if(!path1 || !path2) return false; | ||
|
||
if(path1[path1.length - 1] !== '/') | ||
path1 = path1 + '/'; | ||
if(path2[path2.length - 1] !== '/') | ||
path2 = path2 + '/'; | ||
|
||
return path1 === path2; | ||
} | ||
|
||
module.exports = { | ||
formatLv, | ||
formatPop, | ||
formatDate, | ||
formatDateShort, | ||
formatDateTime, | ||
formatTime, | ||
formatSecs, | ||
format, | ||
checkPaths, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import React, { useState, useEffect, useContext } from 'react'; | ||
|
||
import { Link, useLocation, useParams, useHistory } from 'react-router-dom'; | ||
|
||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; | ||
import { faChevronLeft, faChevronRight, faFastForward, faFastBackward } from '@fortawesome/free-solid-svg-icons'; | ||
|
||
import Loading from '../../layout/Loading'; | ||
import { ContentPanel } from '../Modules'; | ||
|
||
import { AuthContext } from '../../App'; | ||
|
||
import CommentForm from './CommentForm'; | ||
import { PaginationLinks } from './CommentSection'; | ||
import Comment from './Comment'; | ||
|
||
const useQuery = () => { | ||
return new URLSearchParams(useLocation().search); | ||
} | ||
|
||
import styled from 'styled-components'; | ||
|
||
const BackButton = styled(Link)` | ||
cursor: pointer; | ||
border: none; | ||
border-radius: 6px; | ||
background: none; | ||
margin-right: 5px; | ||
font-size: 48px; | ||
color: black; | ||
padding: 5px; | ||
&:hover { | ||
background-color: #eee; | ||
} | ||
`; | ||
|
||
export default props => { | ||
const [data, setData] = useState(null); | ||
const query = useQuery(); | ||
const { violation } = useParams(); | ||
const history = useHistory(); | ||
|
||
const { authGet } = useContext(AuthContext); | ||
|
||
useEffect(() => { | ||
let url = `/violations/${violation}/comments`; | ||
const page = query.get("page"); | ||
const limit = query.get("limit"); | ||
|
||
if(page || limit) url += '?'; | ||
|
||
if(page) url += `page=${page}`; | ||
if(limit) url += `limit=${limit}`; | ||
|
||
authGet(url).then(res => { | ||
setData(res.data); | ||
}); | ||
}, [query.get("page")]); | ||
|
||
const renderLinks = () => { | ||
const firstAvail = data.meta.currentPage !== 1; | ||
const lastAvail = data.meta.currentPage !== data.meta.totalPages; | ||
const nextAvail = data.links.next; | ||
const prevAvail = data.links.previous; | ||
|
||
const nextPage = data.meta.currentPage + 1; | ||
const prevPage = data.meta.currentPage - 1; | ||
|
||
return ( | ||
<PaginationLinks> | ||
<Link className={firstAvail? '' : 'disabled'} to={`/violation/${violation}/comments?page=${1}&limit=20`}> | ||
<FontAwesomeIcon icon={faFastBackward}/> Първа | ||
</Link> | ||
<Link className={prevAvail? '' : 'disabled'} to={`/violation/${violation}/comments?page=${prevPage}&limit=20`}> | ||
<FontAwesomeIcon icon={faChevronLeft}/> Предишна | ||
</Link> | ||
<div style={{margin: '0 5px', display: 'inline-block', color: '#444', width: '60px'}}> | ||
{data.meta.currentPage} / {data.meta.totalPages} | ||
</div> | ||
<Link className={nextAvail? '' : 'disabled'} to={`/violation/${violation}/comments?page=${nextPage}&limit=20`}> | ||
Следваща <FontAwesomeIcon icon={faChevronRight}/> | ||
</Link> | ||
<Link className={lastAvail? '' : 'disabled'} to={`/violation/${violation}/comments?page=${data.meta.totalPages}&limit=20`}> | ||
Последна <FontAwesomeIcon icon={faFastForward}/> | ||
</Link> | ||
</PaginationLinks> | ||
); | ||
}; | ||
|
||
const newComment = comment => { | ||
if(!query.get("page") || query.get("page").toString() === '1') { | ||
if(data.items.length < data.meta.itemsPerPage) { | ||
setData({...data, meta: {...data.meta, totalItems: data.meta.totalItems + 1}, items: [comment, ...data.items]}); | ||
} else if(data.meta.totalItems === data.meta.itemsPerPage) { | ||
history.push(`/violation/${violation}/comments?page=1&limit=20`); | ||
} else { | ||
let newItems = [comment, ...data.items].slice(0, data.meta.itemsPerPage); | ||
setData({...data, meta: {...data.meta, totalItems: data.meta.totalItems + 1}, items: newItems}); | ||
} | ||
} else { | ||
history.push(`/violation/${violation}/comments?page=1&limit=20`); | ||
} | ||
}; | ||
|
||
return ( | ||
<ContentPanel> | ||
<h1> | ||
<BackButton to={`/violation/${violation}`}><FontAwesomeIcon icon={faChevronLeft}/></BackButton> | ||
<span style={{verticalAlign: 'top', marginTop: '10px', display: 'inline-block'}}> | ||
Всички коментари | ||
{!data? null : ` (${data.meta.totalItems})`} | ||
</span> | ||
</h1> | ||
<hr/> | ||
<CommentForm newComment={newComment}/> | ||
{ | ||
!data? <Loading/> : [ | ||
data.meta.totalPages > 1? renderLinks() : null, | ||
data.items.map(comment => [ | ||
<Comment comment={comment}/>, | ||
<hr/> | ||
]), | ||
data.meta.totalPages > 1? renderLinks() : null, | ||
] | ||
} | ||
</ContentPanel> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import React from 'react'; | ||
|
||
import styled from 'styled-components'; | ||
|
||
import { formatTime, formatDateShort } from '../../Util'; | ||
|
||
export const CommentStyle = styled.div` | ||
width: 100%; | ||
h1 { | ||
font-weight: normal; | ||
font-size: 22px; | ||
margin: 5px 0; | ||
color: #555; | ||
} | ||
h2 { | ||
font-weight: normal; | ||
font-size: 16px; | ||
margin: 5px 0; | ||
color: #bbb; | ||
} | ||
p { | ||
color: #333; | ||
} | ||
.comment-type { | ||
color: #888; | ||
border: 3px solid #888; | ||
border-radius: 10px; | ||
font-size: 12px; | ||
padding: 5px; | ||
font-weight: bold; | ||
vertical-align: top; | ||
display: inline-block; | ||
margin-left: 10px; | ||
margin-top: -2px; | ||
} | ||
`; | ||
|
||
export default props => { | ||
console.log(props.comment); | ||
|
||
const formatCommentType = commentType => { | ||
switch(commentType) { | ||
case 'internal': return 'Вътрешен'; break; | ||
default: return 'Вътрешен'; break; | ||
} | ||
}; | ||
|
||
return ( | ||
<CommentStyle> | ||
<h1> | ||
{props.comment.author.firstName} {props.comment.author.lastName} | ||
<span className='comment-type'>{formatCommentType(props.comment.type)}</span> | ||
</h1> | ||
<h2>{formatTime(props.comment.createdAt)} – {formatDateShort(props.comment.createdAt)}</h2> | ||
<p>{props.comment.text}</p> | ||
</CommentStyle> | ||
); | ||
}; |
Oops, something went wrong.