module Portal
( ServiceDefinition(..)
, makePortal
) where
import Prelude hiding (concat)
import Data.ByteString (ByteString, concat)
-- html definitions
preStyle, defaultStyle, preContent, postContent :: ByteString
preStyle = "\n\
\ <!DOCTYPE html> \n\
\ <html> \n\
\ <head> \n\
\ <!-- Base style for inter browser reset --> \n\
\ <style> \n\
\ /* Fixing default stupid margin */ \n\
\ body, h1, h2, h3, h4, p, figure, blockquote, dl, dd { \n\
\ margin: 0; \n\
\ } \n\
\ /* Fixing default stupid behaviour with padding */ \n\
\ *, *::before, *::after { \n\
\ box-sizing: border-box; \n\
\ } \n\
\ /* Fixing default font size increases on mobile */ \n\
\ html { \n\
\ -moz-text-size-adjust: none; \n\
\ -webkit-text-size-adjust: none; \n\
\ text-size-adjust: none; \n\
\ } \n\
\ /* Inherit fonts for inputs and buttons */ \n\
\ input, button, textarea, select { \n\
\ font: inherit; \n\
\ } \n\
\ \n\
\ /* Default style for elements, mimics what I see in firefox on macos right now */ \n\
\ body { \n\
\ background-color: #fff; \n\
\ color: #000; \n\
\ } \n\
\ a { \n\
\ color: #0000EE; \n\
\ } \n\
\ a:visited { \n\
\ color: #551A8B; \n\
\ } \n\
\ </style> \n\
\ \n\
\ <!-- Real style for the page --> \n\
\ <style> \n\
\ "
defaultStyle = " \n\
\ body { \n\
\ padding-top: 30px; \n\
\ \n\
\ font-family: sans-serif; \n\
\ } \n\
\ \n\
\ @media (min-width: 737px) { \n\
\ .content-list { \n\
\ width: 700px; \n\
\ } \n\
\ } \n\
\ @media (max-width: 737px) { \n\
\ .content-list { \n\
\ width: 95%; \n\
\ } \n\
\ } \n\
\ .content-list { \n\
\ margin: auto; \n\
\ \n\
\ display: flex; \n\
\ flex-direction: column; \n\
\ align-items: center; \n\
\ gap: 15px; \n\
\ } \n\
\ \n\
\ a.content-item { \n\
\ width: 100%; \n\
\ border: solid; \n\
\ border-width: 1px; \n\
\ border-color: #999; \n\
\ border-radius: 5px; \n\
\ \n\
\ padding: 15px; \n\
\ \n\
\ display: flex; \n\
\ flex-direction: row; \n\
\ align-items: center; \n\
\ gap: 15px; \n\
\ \n\
\ text-decoration: none; \n\
\ \n\
\ color: #000; \n\
\ } \n\
\ a.content-item:visited { \n\
\ color: #000; \n\
\ } \n\
\ a.content-item:hover { \n\
\ border-color: #00F; \n\
\ } \n\
\ \n\
\ .service-title-and-desc { \n\
\ display: flex; \n\
\ flex-direction: column; \n\
\ align-items: left; \n\
\ } \n\
\ \n\
\ .service-title { \n\
\ font-size: 25px; \n\
\ } \n\
\ a:hover .service-title { \n\
\ text-decoration: underline; \n\
\ } \n\
\ \n\
\ .service-description { \n\
\ font-size: 16px; \n\
\ } \n\
\ \n\
\ img.icon { \n\
\ width: 45px; \n\
\ height: 45px; \n\
\ } \n\
\ .icon-placeholder { \n\
\ width: 45px; \n\
\ height: 45px; \n\
\ } \n\
\ "
preContent = " \n\
\ </style> \n\
\ </head> \n\
\ <body> \n\
\ <div class=content-list> \n\
\ "
postContent = " \n\
\ </div> \n\
\ </body> \n\
\ </html> \n\
\ "
itemHead1, itemHead2, itemNoIcon, itemIcon1, itemIcon2, itemLayout, itemTitle1, itemTitle2, itemDesc1, itemDesc2, itemTail :: ByteString
itemHead1 = "<a href =\""
itemHead2 = "\" class = content-item>"
itemNoIcon = "<div class=icon-placeholder></div>"
itemIcon1 = "<img src=\""
itemIcon2 = "\" class=icon />"
itemLayout = "<div class=service-title-and-desc>"
itemTitle1 = "<div class=service-title>"
itemTitle2 = "</div>"
itemDesc1 = "<div class=service-description>"
itemDesc2 = "</div>"
itemTail = "</div></a>"
data ServiceDefinition = ServiceDefinition
{ url :: ByteString
, title :: ByteString
, description :: ByteString
-- | Path to icon which server can resolve and serve
, icon :: Maybe ByteString
}
deriving (Eq, Show)
makePortal :: [ServiceDefinition] -> ByteString
makePortal services = preStyle <> defaultStyle <> preContent <> content <> postContent
where
content = concat . map renderService $ services
renderService :: ServiceDefinition -> ByteString
renderService s =
itemHead1
<> url s
<> itemHead2
<> case icon s of
Nothing -> itemNoIcon
Just path -> itemIcon1 <> path <> itemIcon2
<> itemLayout
<> itemTitle1
<> title s
<> itemTitle2
<> itemDesc1
<> description s
<> itemDesc2
<> itemTail