Added a bunch of stuff
This commit is contained in:
parent
244b0a7eb7
commit
01bcdabd06
Binary file not shown.
|
|
@ -0,0 +1,2 @@
|
||||||
|
gcc -o console_example console_example.c
|
||||||
|
gcc -o console_json -DJSON_SKIP_WHITESPACE console_json.c json.c
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,65 @@
|
||||||
|
/* Example program showing how to invoke job_wizard
|
||||||
|
* from a simple console program in order to execute a task,
|
||||||
|
* then capture the output and print (without parsing).
|
||||||
|
*
|
||||||
|
* Created by Sally Goldin for SEN-210 on 15 September 2025
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
// Open the redirect file and display the contents
|
||||||
|
void displayOutput(char* redirectFilename)
|
||||||
|
{
|
||||||
|
FILE* pF = NULL;
|
||||||
|
char* textbuffer = NULL;
|
||||||
|
|
||||||
|
pF = fopen(redirectFilename,"r");
|
||||||
|
if (pF != NULL)
|
||||||
|
{
|
||||||
|
// get the size of the file
|
||||||
|
fseek(pF, 0L, SEEK_END);
|
||||||
|
int size = ftell(pF);
|
||||||
|
rewind(pF);
|
||||||
|
textbuffer = calloc(size + 2, sizeof(char)); // +2 for terminating 0
|
||||||
|
if (textbuffer == NULL)
|
||||||
|
{
|
||||||
|
printf("Error allocating space to store results\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fread(textbuffer,sizeof(char),size,pF) != size)
|
||||||
|
{
|
||||||
|
printf("Error reading data from result file\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("RESULTS\n");
|
||||||
|
printf(textbuffer);
|
||||||
|
free(textbuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* main program executes a search with no arguments */
|
||||||
|
int main(int argc, char* argv)
|
||||||
|
{
|
||||||
|
char* userEmail = "sally@cmkl.ac.th";
|
||||||
|
char* outputFile = "output.txt";
|
||||||
|
char jobwizardCmd[2048];
|
||||||
|
int returnCode;
|
||||||
|
|
||||||
|
// Create the command to execute
|
||||||
|
// In your real console-based UI, you probably want different functions
|
||||||
|
// that know about the arguments for different job_wizard commands
|
||||||
|
sprintf(jobwizardCmd,"./job_wizard -task search -email %s > %s 2>&1",userEmail,outputFile);
|
||||||
|
// note that 2>&1 redirects both standard output and standard error to the file output.txt
|
||||||
|
returnCode = system(jobwizardCmd);
|
||||||
|
if (returnCode != 0)
|
||||||
|
{
|
||||||
|
printf("Error %d executing job_wizard command\n");
|
||||||
|
printf("Command: |%s|\n",jobwizardCmd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Success!\n");
|
||||||
|
displayOutput(outputFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,201 @@
|
||||||
|
/* console_json.c
|
||||||
|
* Example program showing how to invoke job_wizard
|
||||||
|
* from a simple console program in order to execute a task,
|
||||||
|
* then capture the output and parse as JSON.
|
||||||
|
*
|
||||||
|
* Uses https://github.com/whyisitworking/C-Simple-JSON-Parser
|
||||||
|
*
|
||||||
|
* Created by Sally Goldin for SEN-210 on 15 September 2025
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "json.h"
|
||||||
|
|
||||||
|
// structure to hold one search result, a job summary
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char job_id[8];
|
||||||
|
char title[64];
|
||||||
|
int is_open; // boolean
|
||||||
|
char date_posted[32];
|
||||||
|
} JOB_SUMMARY_T;
|
||||||
|
|
||||||
|
// Open the redirect file, reads and returns the content
|
||||||
|
// as a null terminated text string. Note that the returned
|
||||||
|
// string must be freed by the caller when no longer needed
|
||||||
|
char* readOutput(char* redirectFilename)
|
||||||
|
{
|
||||||
|
FILE* pF = NULL;
|
||||||
|
char* textbuffer = NULL;
|
||||||
|
|
||||||
|
pF = fopen(redirectFilename,"r");
|
||||||
|
if (pF != NULL)
|
||||||
|
{
|
||||||
|
// get the size of the file
|
||||||
|
fseek(pF, 0L, SEEK_END);
|
||||||
|
int size = ftell(pF);
|
||||||
|
rewind(pF);
|
||||||
|
textbuffer = calloc(size+2, sizeof(char)); // +2 for 0 term
|
||||||
|
if (textbuffer == NULL)
|
||||||
|
{
|
||||||
|
printf("Error allocating space to store results\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (fread(textbuffer,sizeof(char),size,pF) != size)
|
||||||
|
{
|
||||||
|
printf("Error reading data from result file\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (textbuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse the passed string and return the generic structure (json_element)
|
||||||
|
* build by C-Simple-JSON-Parser.
|
||||||
|
* Also returns a status via the integer pointer - 0 if okay, else -1
|
||||||
|
*/
|
||||||
|
typed(json_element) parseJsonString(char* rawJsonText, int* pStatus)
|
||||||
|
{
|
||||||
|
// based on example in the C-Simple-JSON-Parser repo
|
||||||
|
typed(json_element) element;
|
||||||
|
*pStatus = 0; // assume it will work
|
||||||
|
|
||||||
|
result(json_element) element_result = json_parse(rawJsonText);
|
||||||
|
if (result_is_err(json_element)(&element_result))
|
||||||
|
{
|
||||||
|
typed(json_error) error = result_unwrap_err(json_element)(&element_result);
|
||||||
|
fprintf(stderr, "Error parsing JSON: %s\n", json_error_to_string(error));
|
||||||
|
*pStatus = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
element = result_unwrap(json_element)(&element_result);
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempts to parse the string passed as rawJsonText into
|
||||||
|
* an array of structures that represent job summaries.
|
||||||
|
* This is a two step process. The first, which parses the JSON,
|
||||||
|
* will be the same for all job_wizard results. The second extracts
|
||||||
|
* job summary information from the generic structures produced
|
||||||
|
* by the first.
|
||||||
|
* Returns 0 for success, -1 for error
|
||||||
|
* If successful, also allocates and returns an array of results.
|
||||||
|
* This array must be freed by the caller.
|
||||||
|
* Also sets the value of pJobCount
|
||||||
|
*/
|
||||||
|
int parseSearchResults(char* rawJsonText, JOB_SUMMARY_T** allResults,int *pJobCount)
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
int i,j;
|
||||||
|
JOB_SUMMARY_T * resultArray = NULL;
|
||||||
|
|
||||||
|
typed(json_element) element = parseJsonString(rawJsonText,&status);
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
return status; // do we need to free the element?
|
||||||
|
}
|
||||||
|
// we expect an array of jobs
|
||||||
|
typed(json_array) *arr = element.value.as_array;
|
||||||
|
*pJobCount = arr->count;
|
||||||
|
printf("Found %d jobs\n",*pJobCount);
|
||||||
|
resultArray = (JOB_SUMMARY_T*) calloc(*pJobCount,sizeof(JOB_SUMMARY_T));
|
||||||
|
if (resultArray == NULL)
|
||||||
|
{
|
||||||
|
printf("Error allocating job summary structures\n");
|
||||||
|
status = -1;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
*allResults = resultArray;
|
||||||
|
for (j=0; j < *pJobCount; j++)
|
||||||
|
{
|
||||||
|
typed(json_element) element = arr->elements[j];
|
||||||
|
typed(json_object) *obj = element.value.as_object;
|
||||||
|
for (i = 0; i < obj->count; i++)
|
||||||
|
{
|
||||||
|
typed(json_entry) entry = *(obj->entries[i]);
|
||||||
|
typed(json_string) key = entry.key;
|
||||||
|
typed(json_element_value) value = entry.element.value;
|
||||||
|
if (strcmp(key,"job_id") == 0)
|
||||||
|
{
|
||||||
|
strcpy(resultArray[j].job_id,value.as_string);
|
||||||
|
}
|
||||||
|
else if (strcmp(key,"title") == 0)
|
||||||
|
{
|
||||||
|
strcpy(resultArray[j].title,value.as_string);
|
||||||
|
}
|
||||||
|
else if (strcmp(key,"is_open") == 0)
|
||||||
|
{
|
||||||
|
if (value.as_boolean)
|
||||||
|
resultArray[j].is_open = 1;
|
||||||
|
else
|
||||||
|
resultArray[j].is_open = 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(key,"date_posted") == 0)
|
||||||
|
{
|
||||||
|
strcpy(resultArray[j].date_posted,value.as_string);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Unrecognized object key %s\n",key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
json_free(&element);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Display the contents of a job structure as a line of text
|
||||||
|
*/
|
||||||
|
void printJobSummary(JOB_SUMMARY_T job)
|
||||||
|
{
|
||||||
|
printf("%6s %32s %4s %12s\n",
|
||||||
|
job.job_id,job.title,job.is_open? "t" : "f", job.date_posted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* main program executes a search with no arguments */
|
||||||
|
int main(int argc, char* argv)
|
||||||
|
{
|
||||||
|
char* userEmail = "sally@cmkl.ac.th";
|
||||||
|
char* outputFile = "output.txt";
|
||||||
|
char jobwizardCmd[2048];
|
||||||
|
char* resultText = NULL; // holds results, must be freed after use
|
||||||
|
int returnCode;
|
||||||
|
int jobCount;
|
||||||
|
int j;
|
||||||
|
JOB_SUMMARY_T * searchResults = NULL; // array of structs allocated for parsed JSON
|
||||||
|
|
||||||
|
// Create the command to execute
|
||||||
|
// In your real console-based UI, you probably want different functions
|
||||||
|
// that know about the arguments for different job_wizard commands
|
||||||
|
sprintf(jobwizardCmd,"./job_wizard -task search -email %s > %s 2>&1",userEmail,outputFile);
|
||||||
|
// note that 2>&1 redirects both standard output and standard error to the file output.txt
|
||||||
|
returnCode = system(jobwizardCmd);
|
||||||
|
if (returnCode != 0)
|
||||||
|
{
|
||||||
|
printf("Error %d executing job_wizard command\n");
|
||||||
|
printf("Command: |%s|\n",jobwizardCmd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Successfully ran job_wizard!\n");
|
||||||
|
resultText = readOutput(outputFile);
|
||||||
|
if (resultText != NULL)
|
||||||
|
{
|
||||||
|
returnCode = parseSearchResults(resultText,&searchResults,&jobCount);
|
||||||
|
if (returnCode != 0)
|
||||||
|
printf("Error parsing JSON\n");
|
||||||
|
if (searchResults != NULL)
|
||||||
|
{
|
||||||
|
for (j = 0; j < jobCount; j++)
|
||||||
|
printJobSummary(searchResults[j]);
|
||||||
|
free(searchResults);
|
||||||
|
}
|
||||||
|
free(resultText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,163 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
typedef unsigned int bool;
|
||||||
|
#define true (1)
|
||||||
|
#define false (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define typed(name) name##_t
|
||||||
|
|
||||||
|
typedef const char *typed(json_string);
|
||||||
|
typedef bool typed(json_boolean);
|
||||||
|
|
||||||
|
typedef union json_number_value_u typed(json_number_value);
|
||||||
|
typedef signed long typed(json_number_long);
|
||||||
|
typedef double typed(json_number_double);
|
||||||
|
typedef struct json_number_s typed(json_number);
|
||||||
|
typedef union json_element_value_u typed(json_element_value);
|
||||||
|
typedef struct json_element_s typed(json_element);
|
||||||
|
typedef struct json_entry_s typed(json_entry);
|
||||||
|
typedef struct json_object_s typed(json_object);
|
||||||
|
typedef struct json_array_s typed(json_array);
|
||||||
|
|
||||||
|
#define result(name) name##_result_t
|
||||||
|
#define result_ok(name) name##_result_ok
|
||||||
|
#define result_err(name) name##_result_err
|
||||||
|
#define result_is_ok(name) name##_result_is_ok
|
||||||
|
#define result_is_err(name) name##_result_is_err
|
||||||
|
#define result_unwrap(name) name##_result_unwrap
|
||||||
|
#define result_unwrap_err(name) name##_result_unwrap_err
|
||||||
|
#define result_map_err(outer_name, inner_name, value) \
|
||||||
|
result_err(outer_name)(result_unwrap_err(inner_name)(value))
|
||||||
|
#define result_try(outer_name, inner_name, lvalue, rvalue) \
|
||||||
|
result(inner_name) lvalue##_result = rvalue; \
|
||||||
|
if (result_is_err(inner_name)(&lvalue##_result)) \
|
||||||
|
return result_map_err(outer_name, inner_name, &lvalue##_result); \
|
||||||
|
const typed(inner_name) lvalue = result_unwrap(inner_name)(&lvalue##_result);
|
||||||
|
#define declare_result_type(name) \
|
||||||
|
typedef struct name##_result_s { \
|
||||||
|
typed(json_boolean) is_ok; \
|
||||||
|
union { \
|
||||||
|
typed(name) value; \
|
||||||
|
typed(json_error) err; \
|
||||||
|
} inner; \
|
||||||
|
} result(name); \
|
||||||
|
result(name) result_ok(name)(typed(name)); \
|
||||||
|
result(name) result_err(name)(typed(json_error)); \
|
||||||
|
typed(json_boolean) result_is_ok(name)(result(name) *); \
|
||||||
|
typed(json_boolean) result_is_err(name)(result(name) *); \
|
||||||
|
typed(name) result_unwrap(name)(result(name) *); \
|
||||||
|
typed(json_error) result_unwrap_err(name)(result(name) *);
|
||||||
|
|
||||||
|
typedef enum json_element_type_e {
|
||||||
|
JSON_ELEMENT_TYPE_STRING = 0,
|
||||||
|
JSON_ELEMENT_TYPE_NUMBER,
|
||||||
|
JSON_ELEMENT_TYPE_OBJECT,
|
||||||
|
JSON_ELEMENT_TYPE_ARRAY,
|
||||||
|
JSON_ELEMENT_TYPE_BOOLEAN,
|
||||||
|
JSON_ELEMENT_TYPE_NULL
|
||||||
|
} typed(json_element_type);
|
||||||
|
|
||||||
|
typedef enum json_number_type_e {
|
||||||
|
JSON_NUMBER_TYPE_LONG = 0,
|
||||||
|
JSON_NUMBER_TYPE_DOUBLE,
|
||||||
|
} typed(json_number_type);
|
||||||
|
|
||||||
|
union json_number_value_u {
|
||||||
|
typed(json_number_long) as_long;
|
||||||
|
typed(json_number_double) as_double;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct json_number_s {
|
||||||
|
typed(json_number_type) type;
|
||||||
|
typed(json_number_value) value;
|
||||||
|
};
|
||||||
|
|
||||||
|
union json_element_value_u {
|
||||||
|
typed(json_string) as_string;
|
||||||
|
typed(json_number) as_number;
|
||||||
|
typed(json_object) * as_object;
|
||||||
|
typed(json_array) * as_array;
|
||||||
|
typed(json_boolean) as_boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct json_element_s {
|
||||||
|
typed(json_element_type) type;
|
||||||
|
typed(json_element_value) value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct json_entry_s {
|
||||||
|
typed(json_string) key;
|
||||||
|
typed(json_element) element;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct json_object_s {
|
||||||
|
typed(size) count;
|
||||||
|
typed(json_entry) * *entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct json_array_s {
|
||||||
|
typed(size) count;
|
||||||
|
typed(json_element) * elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum json_error_e {
|
||||||
|
JSON_ERROR_EMPTY = 0,
|
||||||
|
JSON_ERROR_INVALID_TYPE,
|
||||||
|
JSON_ERROR_INVALID_KEY,
|
||||||
|
JSON_ERROR_INVALID_VALUE
|
||||||
|
} typed(json_error);
|
||||||
|
|
||||||
|
declare_result_type(json_element_type)
|
||||||
|
declare_result_type(json_element_value)
|
||||||
|
declare_result_type(json_element)
|
||||||
|
declare_result_type(json_entry)
|
||||||
|
declare_result_type(json_string)
|
||||||
|
declare_result_type(size)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parses a JSON string into a JSON element {json_element_t}
|
||||||
|
* with a fallible `result` type
|
||||||
|
*
|
||||||
|
* @param json_str The raw JSON string
|
||||||
|
* @return The parsed {json_element_t} wrapped in a `result` type
|
||||||
|
*/
|
||||||
|
result(json_element) json_parse(typed(json_string) json_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tries to get the element by key. If not found, returns
|
||||||
|
* a {JSON_ERROR_INVALID_KEY} error
|
||||||
|
*
|
||||||
|
* @param object The object to find the key in
|
||||||
|
* @param key The key of the element to be found
|
||||||
|
* @return Either a {json_element_t} or {json_error_t}
|
||||||
|
*/
|
||||||
|
result(json_element)
|
||||||
|
json_object_find(typed(json_object) * object, typed(json_string) key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints a JSON element {json_element_t} with proper
|
||||||
|
* indentation
|
||||||
|
*
|
||||||
|
* @param indent The number of spaces to indent each level by
|
||||||
|
*/
|
||||||
|
void json_print(typed(json_element) * element, int indent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees a JSON element {json_element_t} from memory
|
||||||
|
*
|
||||||
|
* @param element The JSON element {json_element_t} to free
|
||||||
|
*/
|
||||||
|
void json_free(typed(json_element) * element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a string representation of JSON error {json_error_t} type
|
||||||
|
*
|
||||||
|
* @param error The JSON error enum {json_error_t} type
|
||||||
|
* @return The string representation
|
||||||
|
*/
|
||||||
|
typed(json_string) json_error_to_string(typed(json_error) error);
|
||||||
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
JOBWIZARD_API_PORT=8889
|
||||||
|
JOBWIZARD_DB_NAME=database/jobwizard_db
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
To import CSV files into SQLITE:
|
||||||
|
|
||||||
|
sqlite3 [dbname]
|
||||||
|
.mode csv
|
||||||
|
.import [file] [table]
|
||||||
|
|
||||||
|
NOTE that even though the ID columns in the job_wizard DB are flagged as
|
||||||
|
"autoincrement", SQLITE does not really support this. So we have to provide ID
|
||||||
|
values explicitly in the CSV files.
|
||||||
|
|
||||||
|
Above assumes no column headers in the file. I believe there is a way to tell
|
||||||
|
it to skip column headers.
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
1,sally@cmkl.ac.th,Front End Developer,Design and build attractive and highly usable UIs using React/JS,0,0,0,,1,2025-06-27 13:41 +700
|
||||||
|
2,sally@cmkl.ac.th,Back End Developer,Microservices; REST APIs; Go language; Database design and implementation,2,2,40000,,1,2025-06-27 13:45 +700
|
||||||
|
3,joe@cmkl.ac.th,HR Director,Manage onboarding - evaluation - staff retention - staff benefits for small university,3,5,95000,,1,2025-06-27 13:50 +700
|
||||||
|
4,mark@cmkl.ac.th,Executive Secretary,Handle day to day management tasks for university president,1,3,35000,,1,2025-07-02 13:50 +700
|
||||||
|
5,joe@cmkl.ac.th,Student Relations Officer,Assist students with planning study; gather feedback and complaints; interface with curriculum committee,2,3,42000,,1,2025-07-02 13:50 +700
|
||||||
|
6,sally@cmkl.ac.th,Professor,Teaching and research to support the university ,4,5,95000,,1,2025-07-02 13:50 +700
|
||||||
|
7,joe@cmkl.ac.th,Software Project Leader,Allocate tasks to software development team; monitor progress; train new developers; report to managment,3,4,50000,,1,2025-09-15 13:50 +700
|
||||||
|
8,joe@cmkl.ac.th,Graphics Professional,"Create graphics content including imagery, videos, slide decks; acquire photos at university events",3,2,32600,,1,2025-09-15 13:50 +700
|
||||||
|
9,joe@cmkl.ac.th,Janitor,Cleaning and maintenance,1,0,12500,lisa@outlook.com,0,2025-09-16 13:50 +700
|
||||||
|
10,joe@cmkl.ac.th,Driver,Part time - Drive university van on schedule rounds; occasionally chauffer university president,1,3,18500,,1,2025-09-16 13:50 +700
|
||||||
|
11,mark@cmkl.ac.th,CEO ,Top executive for promising tech start-up; compensation includes stock options,4,4,60000,,1,2025-09-17 13:50 +700
|
||||||
|
12,sally@cmkl.ac.th,UX Designer,Design user interfaces for in-house software; guide developers in implementation; handle usability tests,3,3,38000,jim@gmail.com,0,2025-09-17 13:45 +700
|
||||||
|
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,5 @@
|
||||||
|
1,sally@cmkl.ac.th,Sally,Goldin,“0879990088”,5,2025-06-05
|
||||||
|
2,joe@cmkl.ac.th,Joe,Jenkins,“0329871233”,1,2025-06-22
|
||||||
|
3,mark@cmkl.ac.th,Mark,Masters,“0770992324”,3,2025-06-26
|
||||||
|
4,jim@gmail.com,James,Jamison,“0654329809”,2,2025-06-29
|
||||||
|
5,lisa@outlook.com,Lisa,Roberts,“0567876666”,1,2025-09-17
|
||||||
|
Binary file not shown.
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Lab 1: Introduction
|
||||||
|
Designing a user interface for the JobWizard CMD :D
|
||||||
|
|
||||||
|
**Looking at...**
|
||||||
|
- What the user will do
|
||||||
|
- How will they do it?
|
||||||
|
|
||||||
|
*Try to follow the Golden Rule of UIs*
|
||||||
|
|
||||||
|
## FUCK JAVA AND PYTHON. DISGUSTING.
|
||||||
|
|
||||||
|
## Reminder
|
||||||
|
Create account on Figma
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# SEN-210 - Designing User Interface
|
||||||
Loading…
Reference in New Issue