commit ab38cbb654cf4838a163e159f33e67176e99bfe3
parent d161a49c751b7ba37c7d552d111b7b19998054cf
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date: Sat, 30 Mar 2024 09:15:33 -0700
Add delete time function
To do this, I added the entry member to the timesheet struct which
is set to the entry column in the times table. Using this and a new
statement, the interface can provide a button next to displayed
entries to delete them.
Diffstat:
6 files changed, 97 insertions(+), 22 deletions(-)
diff --git a/css/timesheet.css b/css/timesheet.css
@@ -1,10 +1,15 @@
-table, tr, th, td {
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+tr > :nth-child(-n+6) {
border-collapse: collapse;
border-spacing: 0;
border: 1px solid black;
}
-tr:nth-child(even) {
+tr:nth-child(even) > :nth-child(-n+6) {
background-color: #F3F3F3;
}
@@ -13,7 +18,7 @@ th, td {
padding: 0.2rem;
}
-th[scope=col] {
+th[scope=col]:nth-child(-n+6) {
background-color: lightblue;
}
@@ -21,7 +26,7 @@ td > form {
display: inline;
}
-tr:nth-child(even) > td > form > input[type="submit"] {
+tr:nth-child(even) > td:nth-child(-n+6) > form > input[type="submit"] {
background-color: white;
}
diff --git a/pages/main.c b/pages/main.c
@@ -193,12 +193,52 @@ printtime(struct pagedata *pd, struct timesheet *ts)
}
enum kcgi_err
+printtimefunc(struct pagedata *pd, struct timesheet *ts,
+ char *key, char *name)
+{
+ enum kcgi_err status;
+
+ if ((status = khtml_elem(&pd->html, KELEM_TD)) != KCGI_OK ||
+ (status = khtml_elem(&pd->html, KELEM_FORM)) != KCGI_OK ||
+ (status = khtml_attrx(&pd->html, KELEM_INPUT,
+ KATTR_TYPE, KATTRX_STRING, "hidden",
+ KATTR_NAME, KATTRX_STRING, key,
+ KATTR_VALUE, KATTRX_INT, (uint64_t)ts->entry,
+ KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_attrx(&pd->html, KELEM_INPUT,
+ KATTR_TYPE, KATTRX_STRING, "submit",
+ KATTR_VALUE, KATTRX_STRING, name,
+ KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 2)) != KCGI_OK)
+ return status;
+ return KCGI_OK;
+}
+
+enum kcgi_err
+printtimefuncs(struct pagedata *pd, struct timesheet *ts)
+{
+ enum kcgi_err status;
+
+ if (ts->set & StartTimeFlag &&
+ (status = printtimefunc(pd, ts, "delete", "Delete")) != KCGI_OK)
+ return status;
+ return KCGI_OK;
+}
+
+int
+iscurrent(struct pagedata *pd)
+{
+ return (pd->req.fieldmap[KeyPeriod] ?
+ pd->req.fieldmap[KeyPeriod]->parsed.i : 0) == 0;
+}
+
+enum kcgi_err
printtimes(struct pagedata *pd, struct timesheet *times)
{
unsigned int i;
enum kcgi_err status;
char *headers[] = {
- "Date", "Start time", "Break start", "Break end", "End time", "Hours"
+ "Date", "Start time", "Break start", "Break end", "End time", "Hours", ""
};
struct timesheet *p;
@@ -215,7 +255,7 @@ printtimes(struct pagedata *pd, struct timesheet *times)
if ((status = khtml_attr(&pd->html, KELEM_TH,
KATTR_SCOPE, "col", KATTR__MAX)) != KCGI_OK ||
(status = khtml_puts(&pd->html, headers[i])) != KCGI_OK ||
- (i == Len(headers) - 1 && (
+ (i == 5 && (
(status = khtml_attr(&pd->html, KELEM_A,
KATTR_HREF, "#total", KATTR__MAX)) != KCGI_OK ||
(status = khtml_ncr(&pd->html, 0x2193)) != KCGI_OK ||
@@ -233,6 +273,7 @@ printtimes(struct pagedata *pd, struct timesheet *times)
for (; p; p = p->prev) {
if ((status = khtml_elem(&pd->html, KELEM_TR)) != KCGI_OK ||
(status = printtime(pd, p)) != KCGI_OK ||
+ (iscurrent(pd) && (status = printtimefuncs(pd, p)) != KCGI_OK) ||
(status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
return status;
}
@@ -353,17 +394,24 @@ pagemain(struct pagedata *pd)
pd->req.fieldmap[KeyPeriod]->parsed.i : 0;
current = period == 0;
- if (current && pd->req.fieldmap[KeyBreak]) {
- if (breaktime(pd, pd->user->hash))
- err(1, "Unable to break time");
- }
-
- if (current && pd->req.fieldmap[KeyTime]) {
- tf = gettf(pd->req.fieldmap[KeyTime]->parsed.s);
- if (tf < 0)
- err(1, "Invalid time field");
- if (settime(pd, pd->user->hash, tf, time(NULL)))
- return KCGI_SYSTEM;
+ if (current) {
+ if (pd->req.fieldmap[KeyBreak]) {
+ if (breaktime(pd, pd->user->hash))
+ err(1, "Unable to break time");
+ }
+ if (pd->req.fieldmap[KeyTime]) {
+ tf = gettf(pd->req.fieldmap[KeyTime]->parsed.s);
+ if (tf < 0)
+ err(1, "Invalid time field");
+ if (settime(pd, pd->user->hash, tf, time(NULL)))
+ return KCGI_SYSTEM;
+ }
+ if (pd->req.fieldmap[KeyDelete]) {
+ if (!kvalid_uint(pd->req.fieldmap[KeyDelete]))
+ return KCGI_SYSTEM;
+ if (deletetime(pd, pd->user->hash, pd->req.fieldmap[KeyDelete]->parsed.i))
+ return KCGI_SYSTEM;
+ }
}
status = tk_startpage(pd, &template, KHTTP_200);
diff --git a/stmt.c b/stmt.c
@@ -103,11 +103,11 @@ struct sqlbox_pstmt pstmts[] = {
" (SELECT (entry) FROM current_entries WHERE userid IS ?)"
},
[StmtGetTimes] = { .stmt =
- "SELECT period, start, startbreak, endbreak, end FROM times\n"
+ "SELECT period, entry, start, startbreak, endbreak, end FROM times\n"
" WHERE userid IS ? ORDER BY period DESC, entry"
},
[StmtGetTimePeriod] = { .stmt =
- "SELECT period, start, startbreak, endbreak, end FROM times\n"
+ "SELECT period, entry, start, startbreak, endbreak, end FROM times\n"
" WHERE userid IS ? AND period IS ? ORDER BY entry"
},
[StmtBreakTime] = { .stmt =
@@ -115,5 +115,8 @@ struct sqlbox_pstmt pstmts[] = {
" (SELECT IFNULL(max(period) + 1, 1)\n"
" FROM times WHERE userid IS ?)\n"
" WHERE userid IS ? AND period ISNULL"
+ },
+ [StmtDeleteTime] = { .stmt =
+ "DELETE FROM times WHERE userid IS ? AND entry IS ?"
}
};
diff --git a/stmt.h b/stmt.h
@@ -20,6 +20,7 @@ enum StmtID {
StmtGetTimes,
StmtGetTimePeriod,
StmtBreakTime,
+ StmtDeleteTime,
StmtMax
};
diff --git a/times.c b/times.c
@@ -63,6 +63,18 @@ settime(struct pagedata *pd, char *hash, enum time_field f, time_t time)
}
int
+deletetime(struct pagedata *pd, char *hash, size_t entry)
+{
+ struct sqlbox_parm params[] = {
+ { .sparm = hash, .type = SQLBOX_PARM_STRING },
+ { .iparm = entry, .type = SQLBOX_PARM_INT }
+ };
+
+ return sqlbox_exec(pd->db, pd->dbid, StmtDeleteTime,
+ Len(params), params, 0) != SQLBOX_CODE_OK;
+}
+
+int
breaktime(struct pagedata *pd, char *hash)
{
struct sqlbox_parm ps[] = {
@@ -112,6 +124,7 @@ timesheet_set(struct timesheet *ts, enum time_field f, time_t v)
int
gettimes(struct pagedata *pd, char *hash, int period, struct timesheet **rtimes)
{
+ static unsigned int nreturn = 6;
size_t stmtid;
struct sqlbox_parm p[] = {
{ .sparm = hash, .type = SQLBOX_PARM_STRING },
@@ -130,7 +143,7 @@ gettimes(struct pagedata *pd, char *hash, int period, struct timesheet **rtimes)
times = oldtime = NULL;
while ((r = sqlbox_step(pd->db, stmtid)) &&
!(!r->psz && r->code == SQLBOX_CODE_OK)) {
- assert(r->psz == 5);
+ assert(r->psz == nreturn);
oldtime = time;
time = newtimesheet();
if (!time) {
@@ -142,20 +155,23 @@ gettimes(struct pagedata *pd, char *hash, int period, struct timesheet **rtimes)
else if (oldtime)
inserttimesheet(time, oldtime);
- for (i = 0; i < 5; ++i) {
+ for (i = 0; i < nreturn; ++i) {
switch (r->ps[i].type) {
case SQLBOX_PARM_NULL:
break;
case SQLBOX_PARM_INT:
if (i == 0)
time->period = r->ps[i].iparm;
+ else if (i == 1)
+ time->entry = r->ps[i].iparm;
else
- timesheet_set(time, i - 1, r->ps[i].iparm);
+ timesheet_set(time, i - (nreturn - 4), r->ps[i].iparm);
break;
default:
err(1, "This is a stmt.c bug");
}
}
+ assert(i >= (nreturn - 4));
}
if (!sqlbox_finalise(pd->db, stmtid)) {
diff --git a/times.h b/times.h
@@ -19,12 +19,14 @@ struct timesheet {
enum time_flag set; /* which times are set */
unsigned int period; /* period of the times, 0 if not set */
struct timesheet *prev, *next; /* previous and next entry */
+ size_t entry; /* the entry, unique in each user's timesheets */
};
extern char *timefields[];
extern enum time_flag timeflagmap[];
int settime(struct pagedata *pd, char *hash, enum time_field f, time_t time);
+int deletetime(struct pagedata *pd, char *hash, size_t entry);
int breaktime(struct pagedata *pd, char *hash);
void freetimesheet(struct timesheet *ts);
struct timesheet *newtimesheet(void);