Implemented edit and update for entries.
- Added ts_entry:delete/1 to delete an entry from the database. - Implemented ts_api:delete_entry/3. - Added a form to facilitate editing individual entries. - Moved the small show/hide functions directly into the HTML. - Wired up the update timeline form. - Wired up the edit and update entry form.
This commit is contained in:
parent
1b1e31059b
commit
39c3b83d3f
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -482,7 +482,22 @@ post_entry(YArg, Username, TimelineId, EntryId) ->
|
|||||||
_Error -> make_json_500(YArg)
|
_Error -> make_json_500(YArg)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
delete_entry(_YArg, _Username, _TimelineId, _EntryId) -> todo.
|
delete_entry(YArg, Username, TimelineId, EntryId) ->
|
||||||
|
|
||||||
|
% find the record to delete
|
||||||
|
case ts_entry:lookup(Username, TimelineId, EntryId) of
|
||||||
|
|
||||||
|
Record ->
|
||||||
|
% try to delete
|
||||||
|
case ts_entry:delete(Record) of
|
||||||
|
ok -> {status, 200};
|
||||||
|
Error ->
|
||||||
|
io:format("Error occurred deleting entry record: ~p", [Error]),
|
||||||
|
make_json_500(YArg)
|
||||||
|
end;
|
||||||
|
|
||||||
|
no_record -> make_json_404(YArg)
|
||||||
|
end.
|
||||||
|
|
||||||
% ============================== %
|
% ============================== %
|
||||||
% ======== UTIL METHODS ======== %
|
% ======== UTIL METHODS ======== %
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-module(ts_entry).
|
-module(ts_entry).
|
||||||
-export([create_table/1, new/1, update/1, lookup/3, list_asc/3, list_desc/3]).
|
-export([create_table/1, new/1, update/1, delete/1, lookup/3, list_asc/3, list_desc/3]).
|
||||||
|
|
||||||
-include("ts_db_records.hrl").
|
-include("ts_db_records.hrl").
|
||||||
-include_lib("stdlib/include/qlc.hrl").
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
@ -35,6 +35,9 @@ lookup(Username, TimelineId, EntryId) ->
|
|||||||
[Entry] -> Entry
|
[Entry] -> Entry
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
delete(ER = #ts_entry{}) -> mnesia:dirty_delete_object(ER).
|
||||||
|
|
||||||
|
|
||||||
list({Username, Timeline}, Start, Length, OrderFun)
|
list({Username, Timeline}, Start, Length, OrderFun)
|
||||||
when is_integer(Start) and is_integer(Length) ->
|
when is_integer(Start) and is_integer(Length) ->
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ body {
|
|||||||
background: #b34c2b;
|
background: #b34c2b;
|
||||||
color: #c5c5b9;
|
color: #c5c5b9;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
min-width: 2em;
|
width: 2em;
|
||||||
text-align: right; }
|
text-align: right; }
|
||||||
.entry-bar .details {
|
.entry-bar .details {
|
||||||
float: left; }
|
float: left; }
|
||||||
@ -223,6 +223,13 @@ body {
|
|||||||
.entry-bar .details .entry-notes {
|
.entry-bar .details .entry-notes {
|
||||||
display: none;
|
display: none;
|
||||||
padding-left: 1.5em; }
|
padding-left: 1.5em; }
|
||||||
|
.entry-bar .entry-edit {
|
||||||
|
display: none; }
|
||||||
|
.entry-bar .entry-edit .id {
|
||||||
|
width: 2em;
|
||||||
|
padding: 0.2em 0.5em 0.2em 0.5em; }
|
||||||
|
.entry-bar .entry-edit .entry-notes {
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
.top-entry {
|
.top-entry {
|
||||||
-moz-border-radius-topleft: 0.5em;
|
-moz-border-radius-topleft: 0.5em;
|
||||||
|
@ -264,7 +264,7 @@ body {
|
|||||||
background: $obor;
|
background: $obor;
|
||||||
color: lighten($greyTxt, 40%);
|
color: lighten($greyTxt, 40%);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
min-width: 2em;
|
width: 2em;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,6 +283,19 @@ body {
|
|||||||
padding-left: 1.5em;
|
padding-left: 1.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.entry-edit {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
.id {
|
||||||
|
width: 2em;
|
||||||
|
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-notes {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-entry {
|
.top-entry {
|
||||||
|
97
www/js/ts.js
97
www/js/ts.js
@ -25,6 +25,9 @@ $(document).ready(function(){
|
|||||||
buttons: { Login: function(){login()} }
|
buttons: { Login: function(){login()} }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: add a hook to AJAX requests to check for 401 unauth
|
||||||
|
// and re-display the login dialog.
|
||||||
|
|
||||||
// try to load user information for an authenticated user
|
// try to load user information for an authenticated user
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/ts_api/users/",
|
url: "/ts_api/users/",
|
||||||
@ -109,6 +112,7 @@ function loadUser(username) {
|
|||||||
// update the timeline display
|
// update the timeline display
|
||||||
$("#timeline-name").text(activeTimeline.timeline_id + " |");
|
$("#timeline-name").text(activeTimeline.timeline_id + " |");
|
||||||
$("#timeline-desc").text(activeTimeline.description);
|
$("#timeline-desc").text(activeTimeline.description);
|
||||||
|
$("#timeline-desc-input").val(activeTimeline.description);
|
||||||
|
|
||||||
// TODO: populate the drop-down list for the available timeline
|
// TODO: populate the drop-down list for the available timeline
|
||||||
// choices
|
// choices
|
||||||
@ -205,27 +209,12 @@ function displayOlderEntries(entries) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show/hide the editable user-info panel. */
|
|
||||||
function toggleUserInfo(event) {
|
|
||||||
$("#user-info").slideToggle("slow");
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Show/hide the password change controls. */
|
|
||||||
function showChangePwd(event) { $("#change-pwd").slideToggle("slow"); }
|
|
||||||
|
|
||||||
/* Update the user information based on the editable user-info panel. */
|
/* Update the user information based on the editable user-info panel. */
|
||||||
function updateUser(event) {
|
function updateUser(event) {
|
||||||
alert("TODO: update user via AJAX.");
|
alert("TODO: update user via AJAX.");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show/hide the editable timeline-info panel. */
|
|
||||||
function toggleTimelineInfo(event) {
|
|
||||||
$("#timeline-info").slideToggle("slow");
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Show the change timeline menu. */
|
/* Show the change timeline menu. */
|
||||||
function showTimelineMenu(event) {
|
function showTimelineMenu(event) {
|
||||||
alert("TODO: show other timelines via a popup menu");
|
alert("TODO: show other timelines via a popup menu");
|
||||||
@ -234,22 +223,24 @@ function showTimelineMenu(event) {
|
|||||||
|
|
||||||
/* Update the timeline details based on the editable timeline-info panel. */
|
/* Update the timeline details based on the editable timeline-info panel. */
|
||||||
function updateTimeline(event) {
|
function updateTimeline(event) {
|
||||||
alert("TODO: update timeline via AJAX.");
|
var desc = $("#timeline-desc-input").val();
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Show/hide the add notes panel. */
|
$.ajax({url: "/ts_api/timelines/" + user.username
|
||||||
function showNewNotes(event) { $("#add-notes").slideToggle("slow"); }
|
+ "/" + activeTimeline.timeline_id,
|
||||||
|
type: "POST",
|
||||||
|
data: JSON.stringify({desc: desc, created: activeTimeline.created}),
|
||||||
|
|
||||||
function toggleEntryNotes(event, entryId) {
|
error: function(jqXHR, textStatus, error) {
|
||||||
var selector = "#" + entryId + " .entry-notes";
|
// TODO: better error handling
|
||||||
$(selector).slideToggle("slow");
|
alert("Error updating timeline: \n" + jqXHR.responseText); },
|
||||||
event.preventDefault();
|
|
||||||
}
|
success: function(data, testStatus, jqXHR) {
|
||||||
|
// TODO: check for appropriate data.status value
|
||||||
|
|
||||||
|
// update display
|
||||||
|
$("#timeline-desc").text(data.timeline.description);
|
||||||
|
}});
|
||||||
|
|
||||||
function editEntry(event, entryId) {
|
|
||||||
var selector = "#" + entryId;
|
|
||||||
alert("TODO: implement edit entry. Called for '" + selector + "'");
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,9 +284,59 @@ function newEntry(event) {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleEditEntry(event, entryId) {
|
||||||
|
$("#entry-" + entryId + " .entry-display").toggle();
|
||||||
|
$("#entry-" + entryId + " .entry-edit").toggle();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateEntry(event, entryId) {
|
||||||
|
|
||||||
|
var mark = $("#entry-" + entryId + "-mark-input").val();
|
||||||
|
var notes = $("#entry-" + entryId + "-notes-input").val();
|
||||||
|
var timestamp = getUTCTimestamp(); // TODO: define and read from input element
|
||||||
|
|
||||||
|
var payload = JSON.stringify(
|
||||||
|
{ mark: mark, notes: notes, timestamp: timestamp });
|
||||||
|
|
||||||
|
$.ajax({url: "/ts_api/entries/" + user.username
|
||||||
|
+ "/" + activeTimeline.timeline_id
|
||||||
|
+ "/" + entryId,
|
||||||
|
type: "POST",
|
||||||
|
data: payload,
|
||||||
|
|
||||||
|
error: function(jqXHR, textStatus, error) {
|
||||||
|
// TODO: error handling
|
||||||
|
alert("Error updating entry: \n" + jqXHR.responseText); },
|
||||||
|
|
||||||
|
success: function(data, textStatus, jqXHR) {
|
||||||
|
// TODO: check that data.status is appropriate
|
||||||
|
|
||||||
|
// update the entry display
|
||||||
|
$("#entry-" + entryId + " .entry-mark").text(data.entry.mark);
|
||||||
|
$("#entry-" + entryId + " .entry-notes").text(data.entry.notes);
|
||||||
|
}});
|
||||||
|
|
||||||
|
toggleEditEntry(event, entryId);
|
||||||
|
}
|
||||||
|
|
||||||
/* Delete an entry. */
|
/* Delete an entry. */
|
||||||
function deleteEntry(event, entryId) {
|
function deleteEntry(event, entryId) {
|
||||||
|
$.ajax({url: "/ts_api/entries/" + user.username
|
||||||
|
+ "/" + activeTimeline.timeline_id
|
||||||
|
+ "/" + entryId,
|
||||||
|
type: "DELETE",
|
||||||
|
|
||||||
|
error: function(jqXHR, textStatus, error) {
|
||||||
|
// TODO: error handling
|
||||||
|
alert("Error updating entry: \n" + jqXHR.responseText); },
|
||||||
|
|
||||||
|
success: function(data, textStatus, jqXHR) {
|
||||||
|
$("#entry-" + entryId).slideUp('slow',
|
||||||
|
function() {$("#entry-" + entryId).remove(); });
|
||||||
|
}});
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Generate a UTC timestamp string in ISO format.
|
/* Generate a UTC timestamp string in ISO format.
|
||||||
|
@ -16,18 +16,39 @@
|
|||||||
|
|
||||||
<script id="entry" type="text/html">
|
<script id="entry" type="text/html">
|
||||||
<div class="entry-bar" id="entry-{{entry_id}}">
|
<div class="entry-bar" id="entry-{{entry_id}}">
|
||||||
<span class="id">{{entry_id}}</span>
|
<div class="entry-display">
|
||||||
<div class="details">
|
<span class="id">{{entry_id}}</span>
|
||||||
<div class="entry-mark">{{mark}}</div>
|
<div class="details">
|
||||||
<div class="entry-notes">{{notes}}</div>
|
<div class="entry-mark">{{mark}}</div>
|
||||||
|
<div class="entry-notes">{{notes}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="control-links">
|
||||||
|
<a onclick="$('#entry-{{entry_id}} .entry-display .entry-notes').slideToggle('slow');"
|
||||||
|
href="#">show notes</a>
|
||||||
|
<a onclick="toggleEditEntry(event, {{entry_id}})"
|
||||||
|
href="#">edit</a>
|
||||||
|
<a onclick="deleteEntry(event, {{entry_id}})"
|
||||||
|
href="#">del</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-links">
|
<div class="entry-edit">
|
||||||
<a onclick="toggleEntryNotes(event, 'entry-{{entry_id}}')"
|
<form action="/ts/update-entry.yaws"
|
||||||
href="#">show notes</a>
|
onsubmit="updateEntry(event, {{entry_id}})">
|
||||||
<a onclick="editEntry(event, 'entry-{{entry_id}}')"
|
<input type="text" id="entry-{{entry_id}}-id-input"
|
||||||
href="#">edit</a>
|
class="id" value="{{entry_id}}"/>
|
||||||
<a onclick="deleteEntry(event, 'entry-{{entry_id}}')"
|
<div class="details">
|
||||||
href="#">del</a>
|
<input type="text" id="entry-{{entry_id}}-mark-input"
|
||||||
|
class="entry-mark" value="{{mark}}"/></br>
|
||||||
|
<textarea id="entry-{{entry_id}}-notes-input"
|
||||||
|
class="entry-notes" rows="8" cols="40" >{{notes}}</textarea>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="control-links">
|
||||||
|
<a onclick="$('#entry-{{entry_id}} .entry-edit .entry-notes').slideToggle('slow');"
|
||||||
|
href="#">show notes</a>
|
||||||
|
<a onclick="updateEntry(event, {{entry_id}})"
|
||||||
|
href="#">save changes</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
@ -40,7 +61,8 @@
|
|||||||
<span id="username">- no_user</span>
|
<span id="username">- no_user</span>
|
||||||
<div class="control-links">
|
<div class="control-links">
|
||||||
<a href="/ts/edit-user.yaws"
|
<a href="/ts/edit-user.yaws"
|
||||||
onclick="toggleUserInfo(event)">user info</a>
|
onclick="$('#user-info').slideToggle('slow'); return false;">
|
||||||
|
user info</a>
|
||||||
<a href="/ts/logout.yaws" onclick="logout(event)">logout</a>
|
<a href="/ts/logout.yaws" onclick="logout(event)">logout</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -74,7 +96,7 @@
|
|||||||
<label for="enable-pwd-change-input">
|
<label for="enable-pwd-change-input">
|
||||||
<input name="enable-pwd-change" type="checkbox"
|
<input name="enable-pwd-change" type="checkbox"
|
||||||
id="enable-pwd-change-input"
|
id="enable-pwd-change-input"
|
||||||
onclick="showChangePwd(event)"/>
|
onclick="$('#change-pwd').slideToggle('slow');"/>
|
||||||
change password
|
change password
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -93,7 +115,8 @@
|
|||||||
<span id="timeline-desc">timeline description</span>
|
<span id="timeline-desc">timeline description</span>
|
||||||
<div class="control-links">
|
<div class="control-links">
|
||||||
<a href="/ts/edit-timeline.yaws"
|
<a href="/ts/edit-timeline.yaws"
|
||||||
onclick="toggleTimelineInfo(event)">timeline info</a>
|
onclick="$('#timeline-info').slideToggle('slow'); return false;">
|
||||||
|
timeline info</a>
|
||||||
<a href="/ts/select-timeline.yaws"
|
<a href="/ts/select-timeline.yaws"
|
||||||
onclick="showTimelineMenu(event)">change timelines</a>
|
onclick="showTimelineMenu(event)">change timelines</a>
|
||||||
</div>
|
</div>
|
||||||
@ -122,7 +145,8 @@
|
|||||||
class="form-submit" type="submit" value="create entry"/>
|
class="form-submit" type="submit" value="create entry"/>
|
||||||
<div class="control-links">
|
<div class="control-links">
|
||||||
<a id="show-notes" href="#"
|
<a id="show-notes" href="#"
|
||||||
onclick="showNewNotes(event)">add notes</a>
|
onclick="$('#add-notes').slideToggle('slow');">
|
||||||
|
add notes</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="add-notes" class="form-col">
|
<div id="add-notes" class="form-col">
|
||||||
<label for="new-notes-input">notes:</label>
|
<label for="new-notes-input">notes:</label>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user