timestamper/www/index.yaws
Jonathan Bernard a3c55e918e Bugfixes: session management, new entry creation.
* Changed the cookie Path value to allow the cookie to be reused for the
  domain, not just ths `/ts_api` path. This allows the user to refresh the page
  and reuse their existing session as long as it is not stale.
* Fixed a bug in the `ts_json:ejson_to_record_strict/2` function. It was
  expecting a record out of `ts_json:ejson_to_record/2` but that function
  returns a tuple with the record and extended data. Because of the way
  `ejson_to_record_strict` uses case statements to check for specific values it
  was still passing the parsed record and data through, but all the checks were
  being bypassed.
* Fixed bugs in the `index.yaws` bootstrap code for the case where the user
  already has a valid session.
* Added `urlRoot` functions to the Backbone model definitions.
* Changed the behavior of the new entry creation method. We were trying to
  fetch just updated attributes from the server for the model we created, but
  we were pulling all the entries due to the URL backbone was using. This led
  to the new client-side model having all the previous entry models as
  attributes. Ideally we would fix the fetch so that only the one model is
  requested from the server, but we run into a catch-22 because the lookup id
  is not know by the client as it is generated on the server-side. For now I
  have changed this behavior so that we still pull all entries, but we pull
  them into the collection. The collection is then smart enough to update the
  entries that have changed (namely the new one). The server returns the newly
  created entry attributes in response to the POST request that the client
  makes initially, so when I have more time to work on this I plan to do away
  with the fetch after create, and just pull in the data from the server's
  response.
* Changed formatting.
2013-10-22 15:32:22 +00:00

176 lines
7.2 KiB
Plaintext

<!DOCTYPE html>
<html>
<head>
<title>TimeStamper - Simple Time Tracking</title>
<link href='//fonts.googleapis.com/css?family=Anonymous+Pro|Arvo|Bentham|Cantarell|Josefin+Sans' rel='stylesheet' type='text/css'>
<link rel="stylesheet" media="screen" href="css/ts-screen.css" type="text/css"/>
<!-- Needed for IE, but I'm not sure if I'm going to support IE with this tool. -->
<!--<script type="text/javascript" src="/js/json2.js"></script>-->
<!-- PROD -->
<!--
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js"></script>
-->
<!-- DEV -->
<script type="text/javascript" src="js/jquery-1.5.js"></script>
<script type="text/javascript" src="js/showdown.js"></script>
<script type="text/javascript" src="js/underscore.js"></script>
<script type="text/javascript" src="js/ICanHaz.js"></script>
<script type="text/javascript" src="js/backbone.js"></script>
<script type="text/javascript" src="js/ts.js"></script>
<script type="text/javascript">
<erl>
out(YArg) ->
Session = ts_api_session:get_session(YArg),
case Session of not_logged_in -> {html, "//not logged in"}; session_expired -> {html, "//session expired"};
_S ->
Username = element(2, Session),
% get the user
UserResp = ts_api:get_user(YArg, Username),
{content, _, UserJSON} = lists:keyfind(content, 1, UserResp),
UserRecord = ts_user:lookup(Username),
% get the timelines
TimelineResp = ts_api:list_timelines(YArg, Username),
{content, _, TimelineListJSON} = lists:keyfind(content, 1, TimelineResp),
% get the last used timeline if there is one.
SelectedTimeline = case ts_ext_data:get_property(Username, last_timeline) of
not_set -> ts_timeline:list(Username, 0, 1);
T -> T
end,
% get entries for this timeline
EntriesResp =
ts_api:list_entries(YArg, Username, SelectedTimeline),
{content, _, EntryListJSON} = lists:keyfind(content, 1, EntriesResp),
{html, f(
"function bootstrap() {~n"
" var data = {};~n"
" data.user = ~s;~n"
" data.timelines = ~s;~n"
" data.initialTimelineId = ~p;~n"
" data.entries = ~s;~n"
" return data;~n"
"};",
[lists:flatten(UserJSON), lists:flatten(TimelineListJSON),
SelectedTimeline, lists:flatten(EntryListJSON)])}
end.
</erl>
</script>
<script type="text/html" id="userTemplate">
<div class="fullname">{{name}}</div>
<input class="fullname-input" type="text" value="{{name}}"/></div>
<div class="user-menu">
<div class="username"> - {{id}}</div>
<ul class="user-menu-items">
<li><a href="#">logout</a></li>
<li><a href="#">user info</a></li>
</ul>
</div>
</script>
<script type="text/html" id="timelineTemplate">
<span class="timeline-desc">{{description}}</span>
<input class="timeline-desc-input" type="text" value='{{description}}'/>
<div class="drop-menu">
<div class="timeline-id">(&nbsp;{{id}}&nbsp;)</div>
<input class="timeline-id-input" type="text" value='{{id}}'/>
<ul class="drop-menu-items">
<li class="new-timeline-link"><a href="#">
<img src="img/round_plus_icon_16.png"/>new</a></li>
</ul>
</div>
</script>
<script type="text/html" id="timelineLinkTemplate">
<li class="timeline-link"><a href="#">{{id}}</a></li>
</script>
<script type="text/html" id="entryTemplate">
<div class="mark">
<img class="expand-entry" src="img/br_down_icon_16.png"/>
<img class="collapse-entry" src="img/br_up_icon_16.png"/>
<span>{{mark}}</span>
</div>
<input class="mark-input" type="text" value="{{mark}}"/>
<div class="timestamp">{{start}}</div>
<input class="timestamp-input" type="text" value="{{timestamp}}"/>
<div class="duration">{{duration}}</div>
<div class="notes">
<div class="notes-text">{{notes}}</div>
<textarea class="notes-input" rows="10">{{notes}}</textarea>
</div>
</script>
<script type="text/html" id="daySeparatorTemplate">
<div class="day-seperator">
<h4 class="mark">{{separatorLabel}}</h4>
<h5 class="timestamp">start</h5>
<h5 class="duration">duration</h5>
</div>
</script>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head>
<body>
<!-- == LOGIN FORM == -->
<div id="login" class="hidden dialog">
<div class="container">
<h2>Login</h2>
<div><label>Username: </label><input type="text" id="username-input"></input></div>
<div><label>Password: </label><input type="password" id="password-input"></input></div>
<div class="button-panel">
<span class='validate-tips'></span>
<div id="login-button" class="dialog-button"><a href="#">login</a></div>
</div>
</div>
</div>
<!-- == NEW TIMELINE FORM == -->
<div id="new-timeline" class="hidden dialog">
<div class="container">
<h2>Create a new timeline:</h2>
<div><label>Timeline ID: </label><input type="text" id="new-timeline-id"></input></div>
<div><label>Description: </label><input type="text" id="new-timeline-desc"></input></div>
<div class="button-panel">
<span class='validate-tips'></span>
<div id="new-timeline-create" class="dialog-button"><a href="#">create</a></div>
<div id="new-timeline-cancel" class="dialog-button"><a href="#">cancel</a></div>
</div>
</div>
</div>
<div id="top">
<!-- == TIMELINE == -->
<div id="timeline"><!-- replaced on login by app -->
<div class="timeline-desc">Login</div>
</div>
<!-- == USER == -->
<div id="user"><!-- replaced on login by app -->
</div>
</div>
<div id="entry-list">
<div id="new-entry">
<input id="new-entry-input" class="mark-input"
placeholder="Start a new task..." type="text" />
</div>
<div id="entries"></div>
</div>
<div class="footer">
Copyright 2011 <a href="http://www.jdb-labs.com"><span class="logo">JDB Labs</span> LLC.</a>
</div>
</body>
</html>