Add display for prior intervals.
[?]
Aug 22, 2020, 12:20 AM
QMEYU4MWLTSWPWEEOFRLK2IKE64BY3V5X73323WPLCGPP3TPDYGACDependencies
- [2]
J6S23MDGUse server timestamps for interval start and end. - [3]
I2KHGVD4Require project permissions for access to most data. - [4]
BFZN4SUAMake timeline component work. - [5]
QMRKFEPGRefactor QDB to use a free monad algebra instead. - [6]
AL37SVTCImplement payments service endpoints. - [7]
O5FVTOM6Undo JSON silliness, enable a couple more routes. - [8]
HALRDT2FAdded initial auction create route. - [9]
HO2PFRABClient login now handles response correctly. - [10]
EFSXYZPOAutoformat everything with brittany. - [11]
TUA4HMUDUse real API capability for login. - [12]
PT4276XCAdd logout functionality. - [13]
BWN72T44Don't accept work timestamp from an external source. - [14]
RPAJLHMTChange to use UUIDs instead of ints for primary keys. - [15]
EZQG2APBUpdate task list. - [16]
TKGBRIQTLogin component now raises LoginComplete message. - [17]
WZFQDWW4Add retrieval/storage of current exchange rate data to payment recording. - [18]
IPG33FAWAdd billing daemon - [19]
O722AOKEAdd route to allow crediting of events to users/projects. - [20]
B6HWAPDPModularize & update to recent haskoin. - [21]
GMYPBCWEMake docker-compose work. - [22]
LLKTCDRDMinor reorg of aftok.com paths. - [23]
SPJCFHXWUpdate shell scripts to point to https://aftok.com and prompt for input. - [24]
NSRSSSTRUpdate nginx.conf, make aftok host configurable for cli scripts. - [25]
QU5FW67RAdd project selection to time tracker. - [26]
MGOF7IUFUpdate TASKS list to reflect completed projects. - [27]
3LMXT7Z6preventDefault on login form submission. - [28]
BROSTG5KBeginning of modularization of server. - [29]
JXG3FCXYUpgrade ps + halogen versions. - [30]
ZIG57EE6Fix project selection, end log end on project switch. - [31]
NJNMO72SAdd zcash.com submodule and update client to modern halogen. - [32]
WRPIYG3EUse project listing functionality to check for whether we have a cookie. - [33]
EA5BFM5GSplit Login component into its own module. - [34]
NEDDHXUKReformat via stylish-haskell - [35]
XTBSG4C7Adding serveJSON combinator to eliminate some boilerplate from handlers. - [36]
GCVQD44VCreate amends endpoint, switch to UUID primary keys - [37]
BSIUHCGFAdd payment response handler. - [38]
HMDM3B55Implement core of payments/billing infrastructure. - [39]
UOG5H2TWDefault work logging credit to logged-in user. - [*]
RB2ETNIFAdd skeletal PureScript client project. - [*]
ARX7SHY5Begin work on login UI. - [*]
PBD7LZYQPostgres & auth are beginning to function. - [*]
2G3GNDDUEvent logging is now functioning in postgres. - [*]
ADMKQQGCInitial empty Snap project.
Change contents
- edit in client/spago.dhall at line 12
, "format" - replacement in client/src/Aftok/Login.purs at line 12
import Effect.Class as ECimport Effect.Class.Console (log) - edit in client/src/Aftok/Login.purs at line 29
import Aftok.Types (System) - edit in client/src/Aftok/Login.purs at line 32
import Effect.Class.Console (log) - replacement in client/src/Aftok/Login.purs at line 63
. EC.MonadEffect m=> Capability m. Monad m=> System m-> Capability m - replacement in client/src/Aftok/Login.purs at line 67
component caps = H.mkComponentcomponent system caps = H.mkComponent - replacement in client/src/Aftok/Login.purs at line 167
EC.liftEffect $ WE.preventDefault evlift $ system.preventDefault ev - replacement in client/src/Aftok/Project.purs at line 18
import Data.UUID (UUID, parseUUID)import Data.UUID (UUID, parseUUID, toString) - replacement in client/src/Aftok/Project.purs at line 27
import Aftok.Types (APIError(..), parseDate)import Aftok.Types (APIError(..), System, parseDate) - edit in client/src/Aftok/Project.purs at line 35
import Effect.Class.Console (log, error) - replacement in client/src/Aftok/Project.purs at line 37
derive instance newtypeProjectId :: Newtype ProjectId _derive instance projectIdNewtype :: Newtype ProjectId _ - replacement in client/src/Aftok/Project.purs at line 40
pidStr (ProjectId uuid) = show uuidpidStr (ProjectId uuid) = toString uuid - replacement in client/src/Aftok/Project.purs at line 68
. EC.MonadEffect m=> Capability m. Monad m=> System m-> Capability m - replacement in client/src/Aftok/Project.purs at line 72
projectListComponent caps = H.mkComponentprojectListComponent console caps = H.mkComponent - replacement in client/src/Aftok/Project.purs at line 109
Left _ -> error "Could not retrieve project list."Left _ -> lift <<< console.error $ "Could not retrieve project list." - replacement in client/src/Aftok/Project.purs at line 114
log $ "Selected project index " <> show ilift <<< console.log $ "Selected project index " <> show i - replacement in client/src/Aftok/Timeline.purs at line 7
import Control.Monad.Except.Trans (except, withExceptT, runExceptT)import Control.Monad.Except.Trans (withExceptT, runExceptT) - replacement in client/src/Aftok/Timeline.purs at line 11
import Data.Array (cons)import Data.Array (reverse, filter) - replacement in client/src/Aftok/Timeline.purs at line 13[2.388]→[3.301278:301322](∅→∅),[3.2546]→[3.301278:301322](∅→∅),[3.301322]→[3.2675:2739](∅→∅),[3.2675]→[3.2675:2739](∅→∅)
import Data.DateTime (DateTime(..), adjust)import Data.DateTime.Instant (Instant, unInstant, fromDateTime)import Data.Date (Date, year, month, day)import Data.DateTime (DateTime(..), adjust, date)import Data.DateTime.Instant (Instant, unInstant, fromDateTime, toDateTime) - replacement in client/src/Aftok/Timeline.purs at line 17
import Data.Foldable (class Foldable, any, foldMapDefaultR)import Data.Enum (fromEnum)import Data.Foldable (class Foldable, any, foldMapDefaultR, intercalate, foldr, foldl, length)import Data.JSDate as JDimport Data.Map as M - replacement in client/src/Aftok/Timeline.purs at line 22
import Data.Newtype (unwrap)import Data.Newtype (class Newtype, unwrap) - edit in client/src/Aftok/Timeline.purs at line 26
import Data.Tuple (Tuple(..), fst) - edit in client/src/Aftok/Timeline.purs at line 29
- edit in client/src/Aftok/Timeline.purs at line 30
import Type.Proxy (Proxy(..))-- import Text.Format as F -- (format, zeroFill, width) - replacement in client/src/Aftok/Timeline.purs at line 37
import Effect.Now (now, nowDateTime)import Effect.Now (now) - replacement in client/src/Aftok/Timeline.purs at line 39
import Affjax (post, printError)import Affjax.StatusCode (StatusCode(..))import Affjax (get, post) - replacement in client/src/Aftok/Timeline.purs at line 55
import CSS (backgroundColor, border, rgb, solid, borderRadius, left)import CSS (backgroundColor, clear, clearBoth, border, rgb, solid, borderRadius, left) - replacement in client/src/Aftok/Timeline.purs at line 61
import Aftok.Project (Project, Project'(..), ProjectId(..))import Aftok.Types (APIError(..), parseDate)import Aftok.Project (Project, Project'(..), ProjectId(..), pidStr)import Aftok.Types (APIError, System, JsonCompose, decompose, parseDatedResponse) - replacement in client/src/Aftok/Timeline.purs at line 64
import Effect.Class.Console as Cdata Event' i= StartEvent i| StopEvent i - replacement in client/src/Aftok/Timeline.purs at line 68
type Interval ={ start :: Instant, end :: Instanttype Event = Event' Instantderive instance eventFunctor :: Functor Event'instance eventFoldable :: Foldable Event' wherefoldr f b = case _ ofStartEvent a -> f a bStopEvent a -> f a bfoldl f b = case _ ofStartEvent a -> f b aStopEvent a -> f b afoldMap = foldMapDefaultRinstance eventTraversable :: Traversable Event' wheretraverse f = case _ ofStartEvent a -> StartEvent <$> f aStopEvent a -> StopEvent <$> f asequence = traverse identityinstance decodeJsonEvent :: DecodeJson (Event' String) wheredecodeJson json = doobj <- decodeJson jsonevent <- obj .: "event"start' <- traverse (_ .: "eventTime") =<< event .:? "start"stop' <- traverse (_ .: "eventTime") =<< event .:? "stop"note "Only 'stop' and 'start' events are supported." $ (StartEvent <$> start') <|> (StopEvent <$> stop')newtype Interval' i = Interval{ start :: i, end :: i - edit in client/src/Aftok/Timeline.purs at line 99
derive instance intervalEq :: (Eq i) => Eq (Interval' i)derive instance intervalNewtype :: Newtype (Interval' i) _type Interval = Interval' Instantderive instance intervalFunctor :: Functor Interval'instance intervalFoldable :: Foldable Interval' wherefoldr f b (Interval i) = f i.start (f i.end b)foldl f b (Interval i) = f (f b i.start) i.endfoldMap = foldMapDefaultRinstance intervalTraversable :: Traversable Interval' wheretraverse f (Interval i) = interval <$> f i.start <*> f i.endsequence = traverse identityinstance decodeJsonInterval :: DecodeJson (Interval' String) wheredecodeJson json = doobj <- decodeJson jsoninterval <$> obj .: "start" <*> obj .: "end"interval :: forall i. i -> i -> Interval' iinterval s e = Interval { start: s, end: e }data TimeSpan' t= Before t| During (Interval' t)| After ttype TimeSpan = TimeSpan' DateTimederive instance timeSpanFunctor :: Functor TimeSpan'instance timeSpanFoldable :: Foldable TimeSpan' wherefoldr f b = case _ ofBefore a -> f a bDuring x -> foldr f b xAfter a -> f a bfoldl f b = case _ ofBefore a -> f b aDuring x -> foldl f b xAfter a -> f b afoldMap = foldMapDefaultRinstance timeSpanTraversable :: Traversable TimeSpan' wheretraverse f = case _ ofBefore a -> Before <$> f aDuring x -> During <$> traverse f xAfter a -> After <$> f asequence = traverse identity - replacement in client/src/Aftok/Timeline.purs at line 151
{ start :: Instant{ bounds :: Interval - edit in client/src/Aftok/Timeline.purs at line 153
, end :: Instant - replacement in client/src/Aftok/Timeline.purs at line 157
, history :: Array Interval, history :: M.Map Date (Array Interval) - edit in client/src/Aftok/Timeline.purs at line 171
| Unexpected String - edit in client/src/Aftok/Timeline.purs at line 176
Unexpected t -> t - replacement in client/src/Aftok/Timeline.purs at line 187
{ logStart :: ProjectId -> m (Either TimelineError Instant){ timer :: EventSource m TimelineAction, logStart :: ProjectId -> m (Either TimelineError Instant) - edit in client/src/Aftok/Timeline.purs at line 190
, listIntervals :: ProjectId -> TimeSpan -> m (Either TimelineError (Array Interval)) - replacement in client/src/Aftok/Timeline.purs at line 194[3.1170]→[3.3408:3439](∅→∅),[3.3408]→[3.3408:3439](∅→∅),[3.3439]→[3.1171:1191](∅→∅),[3.1191]→[3.3460:3536](∅→∅),[3.3460]→[3.3460:3536](∅→∅),[3.3536]→[3.1192:1229](∅→∅)
:: forall query input output. Capability Aff-> Project.Capability Aff-> H.Component HH.HTML query input output Affcomponent caps pcaps = H.mkComponent:: forall query input output m. Monad m=> System m-> Capability m-> Project.Capability m-> H.Component HH.HTML query input output mcomponent system caps pcaps = H.mkComponent - replacement in client/src/Aftok/Timeline.purs at line 210
{ limits: { start: bottom, current: bottom, end: bottom }, history: []{ limits: { bounds: interval bottom bottom, current: bottom }, history: M.empty - replacement in client/src/Aftok/Timeline.purs at line 216
render :: TimelineState -> H.ComponentHTML TimelineAction Slots Affrender :: TimelineState -> H.ComponentHTML TimelineAction Slots m - replacement in client/src/Aftok/Timeline.purs at line 227
[HH.text "Today's project timeline"][HH.text "Your project timeline"] - replacement in client/src/Aftok/Timeline.purs at line 229
[HH.slot _projectList unit (Project.projectListComponent pcaps) unit (Just <<< ProjectSelected)]][HH.slot _projectList unit (Project.projectListComponent system pcaps) unit (Just <<< ProjectSelected)] - replacement in client/src/Aftok/Timeline.purs at line 232
[lineHtml (intervalHtml st.limits <$> st.history <> fromMaybe st.active),HH.div_([HH.div_ - replacement in client/src/Aftok/Timeline.purs at line 234
[P.classes (ClassName <$> ["btn", "btn-primary", "float-left"])[P.classes (ClassName <$> ["btn", "btn-primary", "float-left", "my-2"]) - replacement in client/src/Aftok/Timeline.purs at line 240
[P.classes (ClassName <$> ["btn", "btn-primary", "float-right"])[P.classes (ClassName <$> ["btn", "btn-primary", "float-right", "my-2"]) - replacement in client/src/Aftok/Timeline.purs at line 246
], lineHtml $ intervalHtml st.limits.bounds <$> currentHistory st] <> ((\(Tuple d xs) -> dateLine st d xs) <$> priorHistory st))] - replacement in client/src/Aftok/Timeline.purs at line 251
eval :: TimelineAction -> H.HalogenM TimelineState TimelineAction Slots output Aff Uniteval :: TimelineAction -> H.HalogenM TimelineState TimelineAction Slots output m Unit - replacement in client/src/Aftok/Timeline.purs at line 254[3.303737]→[3.303737:303838](∅→∅),[3.303838]→[3.3394:3446](∅→∅),[3.3446]→[3.303899:303950](∅→∅),[3.303899]→[3.303899:303950](∅→∅),[3.303950]→[3.2182:2203](∅→∅),[3.2203]→[3.303972:304127](∅→∅),[3.303972]→[3.303972:304127](∅→∅),[3.304127]→[3.866:966](∅→∅),[3.966]→[3.304127:304192](∅→∅),[3.304127]→[3.304127:304192](∅→∅)
dt@(DateTime date t) <- liftEffect nowDateTimelet startOfDay = DateTime date bottomendOfDay = adjust (Days 1.0) startOfDaystartInstant = fromDateTime startOfDaylimits ={ start: startInstant, current: fromDateTime dt, end: maybe startInstant fromDateTime endOfDay}llen = ilen limits.start limits.endclen = ilen limits.start limits.currentH.put $ { limits : limits, history : []dt@(DateTime today t) <- lift system.nowDateTimeH.put $ { limits : { bounds: dateBounds today, current: fromDateTime dt}, history : M.empty - replacement in client/src/Aftok/Timeline.purs at line 262
_ <- H.subscribe timer_ <- H.subscribe caps.timer - replacement in client/src/Aftok/Timeline.purs at line 270
H.modify_ (_ { selectedProject = Just p, history = [] })timeSpan <- Before <$> lift system.nowDateTime -- FIXME, should come from a form controlintervals' <- lift $ caps.listIntervals (unwrap p).projectId timeSpanlet intervals = case intervals' ofLeft err -> [] -- FIXMERight ivals -> ivalsH.modify_ (_ { selectedProject = Just p, history = toHistory intervals }) - replacement in client/src/Aftok/Timeline.purs at line 286
t <- liftEffect nowt <- lift $ system.now - replacement in client/src/Aftok/Timeline.purs at line 289
logStart :: Project -> H.HalogenM TimelineState TimelineAction Slots output Aff UnitlogStart :: Project -> H.HalogenM TimelineState TimelineAction Slots output m Unit - replacement in client/src/Aftok/Timeline.purs at line 293
Left err -> C.log $ "Failed to start timer: " <> show errLeft err -> lift <<< system.log $ "Failed to start timer: " <> show err - replacement in client/src/Aftok/Timeline.purs at line 296
logEnd :: Project -> H.HalogenM TimelineState TimelineAction Slots output Aff UnitlogEnd :: Project -> H.HalogenM TimelineState TimelineAction Slots output m Unit - replacement in client/src/Aftok/Timeline.purs at line 300
Left err -> C.log $ "Failed to stop timer: " <> show errLeft err -> lift <<< system.log $ "Failed to stop timer: " <> show err - edit in client/src/Aftok/Timeline.purs at line 302
dateBounds :: Date -> IntervaldateBounds date =let startOfDay = DateTime date bottomendOfDay = adjust (Days 1.0) startOfDaystartInstant = fromDateTime startOfDayin interval startInstant (maybe startInstant fromDateTime endOfDay)currentHistory:: TimelineState-> Array IntervalcurrentHistory st =let currentDate = date $ toDateTime st.limits.currentin maybe [] identity (M.lookup currentDate st.history) <> fromMaybe st.activepriorHistory:: TimelineState-> Array (Tuple Date (Array Interval))priorHistory st =let currentDate = date $ toDateTime st.limits.currentin reverse <<< filter (not <<< (currentDate == _) <<< fst) $ M.toUnfoldable st.historydateLine:: forall action slots m. TimelineState-> Date-> Array Interval-> H.ComponentHTML action slots mdateLine st d xs =HH.div[][ HH.text $ dateStr d <> ": " <> show (length xs :: Int), lineHtml (intervalHtml (dateBounds d) <$> xs)]dateStr :: Date -> StringdateStr d = (show <<< fromEnum $ year d) <> "-"<> (show <<< fromEnum $ month d) <> "-"<> (show <<< fromEnum $ day d) - edit in client/src/Aftok/Timeline.purs at line 350
clear clearBoth - replacement in client/src/Aftok/Timeline.purs at line 360
. TimelineLimits. Interval - replacement in client/src/Aftok/Timeline.purs at line 363
intervalHtml limits i =intervalHtml (Interval limits) (Interval i) = - edit in client/src/Aftok/Timeline.purs at line 388
data Event i= StartEvent i| StopEvent iderive instance eventFunctor :: Functor Eventinstance eventFoldable :: Foldable Event wherefoldr f b = case _ ofStartEvent a -> f a bStopEvent a -> f a bfoldl f b = case _ ofStartEvent a -> f b aStopEvent a -> f b afoldMap = foldMapDefaultRinstance eventTraversable :: Traversable Event wheretraverse f = case _ ofStartEvent a -> StartEvent <$> f aStopEvent a -> StopEvent <$> f asequence = traverse identityinstance decodeJsonEvent :: DecodeJson (Event String) wheredecodeJson json = doobj <- decodeJson jsonevent <- obj .: "event"start' <- traverse (_ .: "eventTime") =<< event .:? "start"stop' <- traverse (_ .: "eventTime") =<< event .:? "stop"note "Only 'stop' and 'start' events are supported." $ (StartEvent <$> start') <|> (StopEvent <$> stop') - replacement in client/src/Aftok/Timeline.purs at line 392
s { active = s.active <|> Just { start: t, end: t }s { active = s.active <|> Just (interval t t) - replacement in client/src/Aftok/Timeline.purs at line 397
s { history = maybe s.history (\st -> cons { start: st.start, end: t } s.history) s.actives { history = maybes.history(\i -> M.unionWith (<>) (toHistory [interval (unwrap i).start t]) s.history)s.active - replacement in client/src/Aftok/Timeline.purs at line 407
, active = map (_ { end = t }) s.active, active = map (\(Interval i) -> interval i.start t) s.active - replacement in client/src/Aftok/Timeline.purs at line 418[3.6171]→[3.6171:6263](∅→∅),[3.6263]→[2.1937:2077](∅→∅),[2.2077]→[3.6378:6410](∅→∅),[3.6378]→[3.6378:6410](∅→∅),[3.6410]→[2.2078:2588](∅→∅)
result <- post RF.json ("/api/projects/" <> UUID.toString pid <> "/logStart") requestBodyliftEffect <<< runExceptT $ case result ofLeft err -> throwError <<< LogFailure $ Error { status: Nothing, message: printError err }Right r -> case r.status ofStatusCode 403 ->throwError $ LogFailure ForbiddenStatusCode 200 ->withExceptT (LogFailure <<< ParseFailure r.body) $ doevent <- except $ decodeJson r.bodytimeEvent <- traverse parseDate eventcase timeEvent ofStartEvent t -> pure $ fromDateTime tStopEvent _ -> throwError $ "Expected start event, got stop."other ->throwError <<< LogFailure $ Error { status: Just other, message: r.statusText }response <- post RF.json ("/api/user/projects/" <> UUID.toString pid <> "/logStart") requestBodyliftEffect <<< runExceptT $ doevent <- withExceptT LogFailure $ parseDatedResponse responsecase event ofStartEvent t -> pure tStopEvent _ -> throwError <<< Unexpected $ "Expected start event, got stop." - replacement in client/src/Aftok/Timeline.purs at line 428[3.6788]→[3.6788:6878](∅→∅),[3.6878]→[2.2589:2729](∅→∅),[2.2729]→[3.6993:7025](∅→∅),[3.6993]→[3.6993:7025](∅→∅),[3.7025]→[2.2730:3240](∅→∅)
result <- post RF.json ("/api/projects/" <> UUID.toString pid <> "/logEnd") requestBodyliftEffect <<< runExceptT $ case result ofLeft err -> throwError <<< LogFailure $ Error { status: Nothing, message: printError err }Right r -> case r.status ofStatusCode 403 ->throwError $ LogFailure ForbiddenStatusCode 200 ->withExceptT (LogFailure <<< ParseFailure r.body) $ doevent <- except $ decodeJson r.bodytimeEvent <- traverse parseDate eventcase timeEvent ofStartEvent _ -> throwError $ "Expected stop event, got start."StopEvent t -> pure $ fromDateTime tother ->throwError <<< LogFailure $ Error { status: Just other, message: r.statusText }response <- post RF.json ("/api/user/projects/" <> UUID.toString pid <> "/logEnd") requestBodyliftEffect <<< runExceptT $ doevent <- withExceptT LogFailure $ parseDatedResponse responsecase event ofStartEvent _ -> throwError <<< Unexpected $ "Expected stop event, got start."StopEvent t -> pure tnewtype ListIntervalsResponse a = ListIntervalsResponse{ workIndex :: Array ({ intervals :: Array a })}derive instance listIntervalsResponseNewtype :: Newtype (ListIntervalsResponse a) _derive instance listIntervalsResponseFunctor :: Functor ListIntervalsResponseinstance listIntervalsResponseFoldable :: Foldable ListIntervalsResponse wherefoldr f b (ListIntervalsResponse r) = foldr f b (r.workIndex >>= _.intervals)foldl f b (ListIntervalsResponse r) = foldl f b (r.workIndex >>= _.intervals)foldMap = foldMapDefaultRinstance listIntervalsResponseTraversable :: Traversable ListIntervalsResponse wheretraverse f (ListIntervalsResponse r) =let traverseCreditRow r' = ({ intervals: _ }) <$> traverse f r'.intervalsin (ListIntervalsResponse <<< ({ workIndex: _ })) <$> traverse traverseCreditRow r.workIndexsequence = traverse identityinstance listIntervalsResponseDecodeJson :: DecodeJson a => DecodeJson (ListIntervalsResponse a) wheredecodeJson = map ListIntervalsResponse <<< decodeJson_ListIntervalsResponse :: Proxy (JsonCompose ListIntervalsResponse Interval' String)_ListIntervalsResponse = ProxyapiListIntervals :: ProjectId -> TimeSpan -> Aff (Either TimelineError (Array Interval))apiListIntervals pid ts = dots' <- liftEffect $ traverse (JD.toISOString <<< JD.fromDateTime) tslet queryElements = case ts' ofBefore t -> ["before=" <> t]During (Interval x) -> ["after=" <> x.start, "before=" <> x.end]After t -> ["after=" <> t]response <- get RF.json ("/api/user/projects/" <> pidStr pid <> "/workIndex?" <> intercalate "&" queryElements)liftEffect<<< runExceptT<<< map (\(ListIntervalsResponse r) -> r.workIndex >>= (_.intervals))<<< map decompose<<< withExceptT LogFailure$ parseDatedResponse response - replacement in client/src/Aftok/Timeline.purs at line 476
apiCapability = { logStart: apiLogStart, logEnd: apiLogEnd }apiCapability ={ timer: timer, logStart: apiLogStart, logEnd: apiLogEnd, listIntervals: apiListIntervals} - replacement in client/src/Aftok/Timeline.purs at line 485
{ logStart: \_ -> Right <$> liftEffect now{ timer: timer, logStart: \_ -> Right <$> liftEffect now - edit in client/src/Aftok/Timeline.purs at line 488
, listIntervals: \_ _ -> Right <$> pure [] - edit in client/src/Aftok/Timeline.purs at line 490[3.1544]
intervalDate :: Interval -> DateintervalDate = date <<< toDateTime <<< (_.end) <<< unwraptoHistory :: Array Interval -> M.Map Date (Array Interval)toHistory = M.fromFoldableWith (<>) <<< map (\i -> Tuple (intervalDate i) [i]) - edit in client/src/Aftok/Types.purs at line 5
import Control.Monad.Error.Class (throwError)import Control.Monad.Except.Trans (ExceptT, except, withExceptT) - edit in client/src/Aftok/Types.purs at line 8
import Control.Monad.Except.Trans (ExceptT, except) - replacement in client/src/Aftok/Types.purs at line 10
import Data.Argonaut.Decode (decodeJson)import Data.Argonaut.Decode (class DecodeJson, decodeJson) - replacement in client/src/Aftok/Types.purs at line 12
import Data.Either (note)import Data.DateTime.Instant (Instant, fromDateTime)import Data.Functor.Compose (Compose(..))import Data.Either (Either(..), note)import Data.Foldable (class Foldable, foldr, foldl, foldMap) - replacement in client/src/Aftok/Types.purs at line 17
import Data.Maybe (Maybe)import Data.Maybe (Maybe(..))import Data.Newtype (class Newtype, unwrap, over)import Data.Traversable (class Traversable, traverse) - replacement in client/src/Aftok/Types.purs at line 22
import Affjax.StatusCode (StatusCode)import Effect.Aff (Aff)import Effect.Class (liftEffect)import Effect.Now (now, nowDateTime)import Affjax as AJAXimport Affjax (Response, printError)import Affjax.StatusCode (StatusCode(..))import Effect.Class.Console as Cimport Web.Event.Event as WE - edit in client/src/Aftok/Types.purs at line 32
type System m ={ log :: String -> m Unit, error :: String -> m Unit, now :: m Instant, nowDateTime :: m DateTime, preventDefault :: WE.Event -> m Unit}liveSystem :: System AffliveSystem ={ log: liftEffect <<< C.log, error: liftEffect <<< C.error, now: liftEffect now, nowDateTime: liftEffect nowDateTime, preventDefault: liftEffect <<< WE.preventDefault} - edit in client/src/Aftok/Types.purs at line 59
newtype JsonCompose f g a = JsonCompose (Compose f g a)derive instance jsonComposeNewtype :: Newtype (JsonCompose f g a) _instance jsonComposeFunctor :: (Functor f, Functor g) => Functor (JsonCompose f g) wheremap f = over JsonCompose (map f) - edit in client/src/Aftok/Types.purs at line 66
instance jsonComposeFoldable :: (Foldable f, Foldable g) => Foldable (JsonCompose f g) wherefoldr f b = foldr f b <<< unwrapfoldl f b = foldl f b <<< unwrapfoldMap f = foldMap f <<< unwrapinstance jsonComposeTraversable :: (Traversable f, Traversable g) => Traversable (JsonCompose f g) wheretraverse f = map JsonCompose <<< traverse f <<< unwrapsequence = traverse identityinstance jsonComposeDecodeJson :: (DecodeJson (f (g a))) => DecodeJson (JsonCompose f g a) wheredecodeJson json = JsonCompose <<< Compose <$> decodeJson jsondecompose :: forall f g a. JsonCompose f g a -> f (g a)decompose (JsonCompose (Compose fga)) = fga - edit in client/src/Aftok/Types.purs at line 91
decodeDatedJson :: forall t. Traversable t => DecodeJson (t String) => Json -> ExceptT String Effect (t DateTime)decodeDatedJson json = dodecoded <- except $ decodeJson jsontraverse parseDate decoded - edit in client/src/Aftok/Types.purs at line 97[2.4196]
parseDatedResponse:: forall t. Traversable t=> DecodeJson (t String)=> Either AJAX.Error (Response Json)-> ExceptT APIError Effect (t Instant)parseDatedResponse = case _ ofLeft err ->throwError $ Error { status: Nothing, message: printError err }Right r -> case r.status ofStatusCode 403 ->throwError $ ForbiddenStatusCode 200 ->withExceptT (ParseFailure r.body) $ map fromDateTime <$> decodeDatedJson r.bodyother ->throwError $ Error { status: Just other, message: r.statusText } - edit in client/src/Main.purs at line 11
import Effect.Aff (Aff) - edit in client/src/Main.purs at line 22
import Aftok.Types (System, liveSystem) - replacement in client/src/Main.purs at line 34
mainComponent = component login timeline projectmainComponent = component liveSystem login timeline project - replacement in client/src/Main.purs at line 56[3.308288]→[3.308288:308345](∅→∅),[3.308345]→[3.1759:1788](∅→∅),[3.1788]→[3.3952:3980](∅→∅),[3.1788]→[3.308345:308393](∅→∅),[3.3980]→[3.308345:308393](∅→∅),[3.308345]→[3.308345:308393](∅→∅),[3.308393]→[3.3981:4028](∅→∅)
:: forall query input output. Login.Capability Aff-> Timeline.Capability Aff-> Project.Capability Aff-> H.Component HH.HTML query input output Affcomponent loginCap tlCap pCap = H.mkComponent:: forall query input output m. Monad m=> System m-> Login.Capability m-> Timeline.Capability m-> Project.Capability m-> H.Component HH.HTML query input output mcomponent system loginCap tlCap pCap = H.mkComponent - replacement in client/src/Main.purs at line 74
render :: MainState -> H.ComponentHTML MainAction Slots Affrender :: MainState -> H.ComponentHTML MainAction Slots m - replacement in client/src/Main.purs at line 81
[ HH.slot _login unit (Login.component loginCap) unit (Just <<< LoginComplete) ][ HH.slot _login unit (Login.component system loginCap) unit (Just <<< LoginComplete) ] - replacement in client/src/Main.purs at line 85
[ HH.slot _timeline unit (Timeline.component tlCap pCap) unit absurd ][ HH.slot _timeline unit (Timeline.component system tlCap pCap) unit absurd ] - replacement in client/src/Main.purs at line 87
eval :: MainAction -> H.HalogenM MainState MainAction Slots output Aff Uniteval :: MainAction -> H.HalogenM MainState MainAction Slots output m Unit - file addition: list_intervals.sh[43.1220]
#!/bin/bashif [ -f ".env" ]; thensource .envfiif [ -z "${AFTOK_HOST}" ]; thenAFTOK_HOST="aftok.com"fiif [ -z "${USER}" ]; thenread -p "Username: " USERechofiif [ -z "${PID}" ]; thenread -p "Project UUID: " PIDechoficurl --verbose --insecure --user $USER \--request GET \"https://$AFTOK_HOST/api/projects/$PID/intervals" - replacement in scripts/log_end.sh at line 24
"https://$AFTOK_HOST/api/projects/$PID/logEnd"[3.1202]"https://$AFTOK_HOST/api/user/projects/$PID/logEnd" - replacement in scripts/log_start.sh at line 24
"https://$AFTOK_HOST/api/projects/$PID/logStart"[3.1376]"https://$AFTOK_HOST/api/user/projects/$PID/logStart" - replacement in server/Aftok/Snaplet/WorkLog.hs at line 81[3.6348]→[3.35930:36007](∅→∅),[3.1618]→[3.3428:3456](∅→∅),[3.18605]→[3.3428:3456](∅→∅),[3.36007]→[3.3428:3456](∅→∅),[3.6393]→[3.3428:3456](∅→∅)
loggedIntervalsHandler :: S.Handler App App (WorkIndex (NetworkId, Address))loggedIntervalsHandler = doprojectWorkIndex :: S.Handler App App (WorkIndex (NetworkId, Address))projectWorkIndex = do - replacement in server/Aftok/Snaplet/WorkLog.hs at line 87[3.6632]→[3.36008:36079](∅→∅),[3.18656]→[3.4907:4930](∅→∅),[3.36079]→[3.4907:4930](∅→∅),[3.4907]→[3.4907:4930](∅→∅)
logEntriesHandler :: S.Handler App App [LogEntry (NetworkId, Address)]logEntriesHandler = douserLogEntries :: S.Handler App App [LogEntry (NetworkId, Address)]userLogEntries = do - replacement in server/Aftok/Snaplet/WorkLog.hs at line 98
"You must at least one of the \"after\" or \"before\" query parameter""You must specify at least one of the \"after\" or \"before\" query parameters" - edit in server/Aftok/Snaplet/WorkLog.hs at line 101
userWorkIndex :: S.Handler App App (WorkIndex (NetworkId, Address))userWorkIndex =workIndex <$> userLogEntries - replacement in server/Main.hs at line 71
projectRoute = serveJSON projectJSON $ method GET projectGetHandlerlogEntriesRoute =serveJSON (fmap $ logEntryJSON nmode) $ method GET logEntriesHandlerlogIntervalsRoute =serveJSON (workIndexJSON nmode) $ method GET loggedIntervalsHandlerprojectRoute =serveJSON projectJSON $ method GET projectGetHandlerprojectWorkIndexRoute =serveJSON (workIndexJSON nmode) $ method GET projectWorkIndex - replacement in server/Main.hs at line 76
payoutsRoute = serveJSON (payoutsJSON nmode) $ method GET payoutsHandlerprojectPayoutsRoute = serveJSON (payoutsJSON nmode) $ method GET payoutsHandler - replacement in server/Main.hs at line 80
logWorkBTCRoute f =serveJSON eventIdJSON $ method POST (logWorkBTCHandler f)amendEventRoute = serveJSON amendmentIdJSON $ method PUT amendEventHandler-- logWorkBTCRoute f =-- serveJSON eventIdJSON $ method POST (logWorkBTCHandler f)amendEventRoute =serveJSON amendmentIdJSON $ method PUT amendEventHandleruserLogEntriesRoute =serveJSON (fmap $ logEntryJSON nmode) $ method GET userLogEntriesuserWorkIndexRoute =serveJSON (workIndexJSON nmode) $ method GET userWorkIndex - replacement in server/Main.hs at line 119
, ("projects/:projectId/logStart/:btcAddr", logWorkBTCRoute StartWork), ("projects/:projectId/logEnd/:btcAddr", logWorkBTCRoute StopWork), ("projects/:projectId/logStart" , logWorkRoute StartWork), ("projects/:projectId/logEnd" , logWorkRoute StopWork), ("projects/:projectId/logEntries" , logEntriesRoute), ("projects/:projectId/intervals" , logIntervalsRoute), ( "projects/:projectId/auctions", auctionCreateRoute) -- <|> auctionListRoute, ( "projects/:projectId/billables", billableCreateRoute <|> billableListRoute), ("projects/:projectId/payouts", payoutsRoute), ("projects/:projectId/invite" , inviteRoute), ("projects/:projectId" , projectRoute), ("projects" , projectCreateRoute <|> projectListRoute)-- , ("projects/:projectId/logStart/:btcAddr" , logWorkBTCRoute StartWork)-- , ("projects/:projectId/logEnd/:btcAddr" , logWorkBTCRoute StopWork), ("user/projects/:projectId/logStart" , logWorkRoute StartWork), ("user/projects/:projectId/logEnd" , logWorkRoute StopWork), ("user/projects/:projectId/logEntries" , userLogEntriesRoute), ("user/projects/:projectId/workIndex" , userWorkIndexRoute), ("projects/:projectId/workIndex" , projectWorkIndexRoute), ("projects/:projectId/auctions" , auctionCreateRoute) -- <|> auctionListRoute), ("projects/:projectId/billables" , billableCreateRoute <|> billableListRoute), ("projects/:projectId/payouts" , projectPayoutsRoute), ("projects/:projectId/invite" , inviteRoute), ("projects/:projectId" , projectRoute), ("projects" , projectCreateRoute <|> projectListRoute)