diff --git a/mobile/library_management/.gitignore b/mobile/library_management/.gitignore new file mode 100644 index 0000000..3a85790 --- /dev/null +++ b/mobile/library_management/.gitignore @@ -0,0 +1,3 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ diff --git a/mobile/library_management/CHANGELOG.md b/mobile/library_management/CHANGELOG.md new file mode 100644 index 0000000..effe43c --- /dev/null +++ b/mobile/library_management/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/mobile/library_management/README.md b/mobile/library_management/README.md new file mode 100644 index 0000000..3816eca --- /dev/null +++ b/mobile/library_management/README.md @@ -0,0 +1,2 @@ +A sample command-line application with an entrypoint in `bin/`, library code +in `lib/`, and example unit test in `test/`. diff --git a/mobile/library_management/analysis_options.yaml b/mobile/library_management/analysis_options.yaml new file mode 100644 index 0000000..dee8927 --- /dev/null +++ b/mobile/library_management/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/mobile/library_management/bin/library_management.dart b/mobile/library_management/bin/library_management.dart new file mode 100644 index 0000000..476f1d2 --- /dev/null +++ b/mobile/library_management/bin/library_management.dart @@ -0,0 +1,959 @@ +// import +import 'dart:async'; +import 'dart:developer'; +import 'dart:io'; +import 'dart:isolate'; + +// enum +enum BookStatus { available, loaned, reserved } + +enum MemberTier { standard, premium } + +// enum + + + + + + + + + + + + +// exceptions +class BookNotFoundException implements Exception { + final String message; + BookNotFoundException(this.message); + @override + String toString() => message; +} + +class MemberNotFoundException implements Exception { + final String message; + MemberNotFoundException(this.message); + @override + String toString() => message; +} + +class InvalidLoanException implements Exception { + final String message; + InvalidLoanException(this.message); + @override + String toString() => message; +} + +// exceptions + + + + + + + + + + + +// mixins +mixin Timestampable { + DateTime? createDat; + DateTime? updateDat; + void markCreated() {} + void markUpdated() {} + String getTimestampInfo(); +} +mixin Searchable { + bool matchesQuery(String query); + List getSearchableFields(); +} + +// mixins + + + + + + + + + + + + +// abstract +abstract class LibraryItem { + String id; + String title; + LibraryItem(this.id, this.title); + String getDetails(); + Map toMap(); + void printInfo(); +} + +abstract class Borrowable { + bool isAvailable(); + void borrow(String memberId); + void returnItem(); +} + +// abstract + + + + +// classes + +class Book extends LibraryItem + with Timestampable, Searchable + implements Borrowable { + String author; + String? isbn; + String? description; + BookStatus status; + Set genreTags; + String? currentBorrowerTd; + int pages; + Book(String id, String title, this.author) + : status = BookStatus.available, + genreTags = {}, + pages = 0, + super(id, title); + Book.fromMap(Map map) + : author = map['author'] ?? '', + isbn = map['isbn'], + description = map['description'], + status = map['status'] ?? BookStatus.available, + genreTags = Set.from(map['genreTags'] ?? []), + currentBorrowerTd = map['currentBorrowerTd'], + pages = map['pages'] ?? 0, + super(map['id'] ?? '', map['title'] ?? ''); + Book.withGenres( + String id, + String title, + this.author, { + Set? initialGenres, + }) : status = BookStatus.available, + genreTags = initialGenres ?? {}, + pages = 0, + super(id, title); + @override + String getDetails() { + return "ID: $id | Title: $title | Author: $author | Status: $status"; + } + + @override + Map toMap() { + return { + "id": id, + "title": title, + "author": author, + "isbn": isbn, + "description": description, + "status": status, + "genreTags": genreTags.toList(), + "pages": pages, + }; + } + + @override + bool isAvailable() { + if (this.status == BookStatus.available) return true; + return false; + } + + @override + void borrow(String memberId) { + if (!isAvailable()) { + BookNotFoundException('not fond book'); + return; + } + this.status = BookStatus.loaned; + currentBorrowerTd = memberId; + markUpdated(); + } + + @override + void returnItem() { + this.currentBorrowerTd = null; + this.status = BookStatus.available; + markUpdated(); + } + + @override + bool matchesQuery(String query) { + query = query.toLowerCase(); + return title.toLowerCase().contains(query) || + author.toLowerCase().contains(query) || + genreTags.any((g) => g.toLowerCase().contains(query)); + } + + @override + List getSearchableFields() { + return [title, author, ...genreTags]; + } + + void updateStatus(BookStatus newStatus) { + this.status = newStatus; + } + + void addGenre(String genre) { + this.genreTags.add(genre); + } + + String getStatusText() { + return status.toString().split('.').last; + } + @override +void printInfo() { + print(getDetails()); +} + +@override +String getTimestampInfo() { + return "Created at: ${createDat ?? 'unknown'}, Updated at: ${updateDat ?? 'unknown'}"; +} + +@override +void markCreated() { + createDat = DateTime.now(); +} + +@override +void markUpdated() { + updateDat = DateTime.now(); +} +} + +class Member with Timestampable { + String id; + String name; + String? email; + String? phone; + MemberTier tier; + List loanHistory; + Member(this.id, this.name, this.tier) + : phone = null, + loanHistory = [], + email = null; + Member.fromMap(Map map) + : id = map['id'], + name = map['name'], + email = map['email'], + phone = map['phone'], + tier = map['tier'] ?? MemberTier.standard, + loanHistory = List.from(map['loanHistory'] ?? []); + + Member.premium(String id, String name, {String? email}) + : id = id, + name = name, + email = email, + phone = null, + tier = MemberTier.premium, + loanHistory = []; + Map toMap() { + return { + "id": id, + "name": name, + "email": email, + "phone": phone, + "tier": tier, + "loanHistory": loanHistory, + }; + } + + void addToHistory(String bookId) { + loanHistory.add(bookId); + } + + int getMaxLoans() { + if (this.tier == MemberTier.standard) return 3; + return 10; + } + + bool canBorrow() { + if (this.loanHistory.length == this.getMaxLoans()) return false; + if (this.loanHistory.length > this.getMaxLoans()) return false; + return true; + } + + String getMemberInfo() { + return 'name : ${this.name} email : ${this.email} id : ${this.id} phone : ${this.phone} tier : ${tier}'; + } + + void upgradeTier() { + if (this.tier == MemberTier.standard) { + this.tier = MemberTier.premium; + } + } + @override +@override +String getTimestampInfo() { + return "Created at: ${createDat ?? 'unknown'}, Updated at: ${updateDat ?? 'unknown'}"; +} + +@override +void markCreated() { + createDat = DateTime.now(); +} + +@override +void markUpdated() { + updateDat = DateTime.now(); +} +} + +class Loan { + String loanId; + String bookId; + String memberId; + DateTime loanDate; + DateTime? dueDate; + DateTime? returnDate; + bool isReturned; + Loan(this.loanId, this.bookId, this.memberId) + : loanDate = DateTime.now(), + dueDate = null, + returnDate = null, + isReturned = false; + Loan.withDueDate( + String loanId, + String bookId, + String memberId, { + int dueDays = 14, + }) : loanId = loanId, + bookId = bookId, + memberId = memberId, + loanDate = DateTime.now(), + dueDate = DateTime.now().add(Duration(days: dueDays)), + returnDate = null, + isReturned = false; + Loan.fromMap(Map map) + : loanId = map['loanId'], + bookId = map['bookId'], + memberId = map['memberId'], + loanDate = DateTime.parse(map['loanDate']), + dueDate = map['dueDate'] != null ? DateTime.parse(map['dueDate']) : null, + returnDate = map['returnDate'] != null + ? DateTime.parse(map['returnDate']) + : null, + isReturned = map['isReturned'] ?? false; + Map toMap() { + return { + "loanId": loanId, + "bookId": bookId, + "memberId": memberId, + "loanDate": loanDate.toIso8601String(), + "dueDate": dueDate?.toIso8601String(), + "returnDate": returnDate?.toIso8601String(), + "isReturned": isReturned, + }; + } + + void markReturned() { + this.isReturned = true; + this.returnDate = DateTime.now(); + return; + } + + bool isOverdue() { + if (this.isReturned) return false; + if (this.dueDate == null) return false; + return DateTime.now().isAfter(dueDate!); + } + + int getDaysOverdue() { + if (!isOverdue()) return 0; + return DateTime.now().difference(dueDate!).inDays; + } + + String getLoanInfo() { + return ' id : ${this.loanId} book id : ${this.bookId} member id : ${this.memberId} laon date :${this.loanDate} duedate : ${this.dueDate} returnData ${returnDate} isReturned : ${this.isReturned}'; + } +} + + +// classes + + + + + + + +// manager +class LibraryManager { + Map _books; + Map _members; + List _loans; + StreamController _activityStream; + int _bookCounter; + int _memberCounter; + int _loanCounter; + + LibraryManager() + : _books = {}, + _members = {}, + _loans = [], + _activityStream = StreamController.broadcast(), + _bookCounter = 0, + _memberCounter = 0, + _loanCounter = 0; + + Future addBook(Book book) async { + _bookCounter++; + _books['BOOK$_bookCounter'] = book; + } + + Future findBook(String id) async { + try { + return _books.values.firstWhere((e) => e.id == id); + } catch (e) { + return null; + } + } + + Future> getAllBooks() async { + List books = _books.values.toList(); + return books; + } + + Future updateBook(String id, Book updatedBook) async { + Book? mybook = await findBook(id); + if (mybook == null) return; + final bok = _books.entries.firstWhere((entry) => entry.value == mybook); + if (bok.key.isEmpty) return; + _books[bok.key] = updatedBook; + } + + Future deleteBook(String id) async { + Book? mybook = await findBook(id); + if (mybook == null) return; + _books.removeWhere((key, value) => value == mybook); + } + + Future> getAvailableBooks() async { + List books = await getAllBooks(); + books = books.where((e) => e.status == BookStatus.available).toList(); + return books; + } + + Future> getBooksByGenre(String genre) async { + List books = await getAllBooks(); + books = books.where((e) => e.genreTags.contains(genre)).toList(); + return books; + } + + Future> getBooksByAuthor(String author) async { + List books = await getAllBooks(); + books = books.where((e) => e.author == author).toList(); + return books; + } + + Future addMember(Member member) async { + _memberCounter++; + _members['member$_memberCounter'] = member; + } + + Future findMember(String id) async { + try { + return _members.values.firstWhere((e) => e.id == id); + } catch (e) { + return null; + } + } + + Future> getAllMembers() async { + List members = _members.values.toList(); + return members; + } + + Future updateMember(String id, Member updatedMember) async { + Member? member = await findMember(id); + if (member == null) return; + final meb = _members.entries.firstWhere((e) => e.value == member); + if (meb.key.isEmpty) return; + _members[meb.key] = updatedMember; + } + + Future deleteMember(String id) async { + Member? member = await findMember(id); + if (member == null) return; + _members.removeWhere((key, value) => value == member); + } + + Future> getPremiumMembers() async { + List members = await getAllMembers(); + members = members.where((e) => e.tier == MemberTier.premium).toList(); + return members; + } + + Future> getStandardMembers() async { + List members = await getAllMembers(); + members = members.where((e) => e.tier == MemberTier.standard).toList(); + return members; + } + +Future loanBook(String bookId, String memberId) async { + Book? book = await findBook(bookId); + if (book == null) { + throw BookNotFoundException('Book not found $bookId'); + } + Member? member = await findMember(memberId); + if (member == null) { + throw MemberNotFoundException('Member not found $memberId'); + } + _validateLoan(book, member); + _loanCounter++; + String loanId = "LOAN$_loanCounter"; + Loan loan = Loan.withDueDate(loanId, bookId, memberId); + _loans.add(loan); + book.borrow(memberId); + member.addToHistory(bookId); + _logActivity("Book $bookId loaned to $memberId"); +} +Future returnBook(String loanId) async { + Loan? loan = await findLoan(loanId); + if (loan == null) return; + loan.markReturned(); + Book? book = await findBook(loan.bookId); + if (book != null) { + book.returnItem(); + } + _logActivity("Loan $loanId returned"); +} + + Future reserveBook(String bookId, String memberId) async { + Book? book = await findBook(bookId); + if (book == null) { + BookNotFoundException('book not foond'); + return; + } + if (book.status != BookStatus.available) return; + Member? member = await findMember(memberId); + if (member == null) { + MemberNotFoundException('member not fond'); + return; + } + } + + Future> getActiveLoans() async { + return _loans.where((e) => !e.isReturned).toList(); + } + + Future> getOverdueLoans() async { + return _loans.where((e) => e.isOverdue()).toList(); + } + + Future> getMemberLoans(String memberId) async { + return _loans.where((e) => e.memberId == memberId).toList(); + } + + Future findLoan(String loanId) async { + try { + return _loans.firstWhere((e) => e.loanId == loanId); + } catch (_) { + return null; + } + } + + Future> searchCatalog(String query) async { + return _books.values.where((b) => b.matchesQuery(query)).toList(); + } + + Future> advancedSearch({ + String? title, + String? author, + String? genre, + }) async { + return _books.values.where((b) { + bool match = true; + if (title != null) match &= b.title.contains(title); + if (author != null) match &= b.author.contains(author); + if (genre != null) match &= b.genreTags.contains(genre); + return match; + }).toList(); + } + + int getTotalBooks() { + return _bookCounter; + } + + int getTotalMembers() { + return _memberCounter; + } + + int getActiveLoansCount() { + return this._loans.length; + } + + Map getBookStatusDistribution() { + Map result = {}; + for (var b in _books.values) { + result[b.status] = (result[b.status] ?? 0) + 1; + } + return result; + } + + Map getMemberTierDistribution() { + Map result = {}; + for (var m in _members.values) { + result[m.tier] = (result[m.tier] ?? 0) + 1; + } + return result; + } + + List getMostBorrowedBooks() { + return _books.values.toList(); + } + + Stream get activityLog => _activityStream.stream; + + void _logActivity(String message) { + _activityStream.add(message); + } + + void dispose() { + _activityStream.close(); + } + + void _validateLoan(Book book, Member member) { + if (!book.isAvailable()) { + throw InvalidLoanException("Book not available"); + } + if (!member.canBorrow()) { + throw InvalidLoanException("Member reached limit"); + } + } +} +// manager + + +// helpers +String? readInput(String prompt) { + print(prompt); + return stdin.readLineSync(); +} + +int? readInt(String prompt) { + return int.tryParse(readInput(prompt) ?? ''); +} + +String readRequiredInput(String prompt) { + while (true) { + String input = readInput(prompt) ?? 'null'; + if (input != 'null') { + return input; + } + } +} + +int readRequiredInt(String prompt) { + while (true) { + int num = readInt(prompt) ?? -1; + if (num != -1) { + return num; + } + } +} + +BookStatus selectBookStatus() { + return BookStatus.available; +} + +MemberTier selectMemberTier() { + return MemberTier.standard; +} + +bool confirm(String message) { + return true; +} + +void printHeader(String title) { + print("===== $title ====="); +} + +void printSeparator() { + print("----------------------------"); +} + +void printSuccess(String message) { + print("SUCCESS: $message"); +} + +void printError(String message) { + print("ERROR: $message"); +} + +void printInfo(String message) { + print("INFO: $message"); +} + +void printLoanDetails(Loan loan) { + print(loan.getLoanInfo()); +} + +void printTable(List> data, List headers) { + print(headers.join(" | ")); + for (var row in data) { + print(row.join(" | ")); + } +} + +Map bookToMap(Book book) { + Map c = { + 'id': book.id, + 'title': book.title, + 'author': book.author, + 'isbn': book.isbn, + 'description': book.description, + 'statue': book.status, + 'genreTags': book.genreTags, + 'currentBorrowerTd': book.currentBorrowerTd, + 'pages': book.pages, + 'createDat': book.createDat, + 'updateDat': book.updateDat, + }; + return c; +} + +Book mapToBook(Map map) { + Book c = Book(map['id'] ?? '', map['title'] ?? '', map['author'] ?? ''); + c.isbn = map['isbn']; + c.description = map['description']; + c.status = map['status']; + c.genreTags = map['genreTags']; + c.currentBorrowerTd = map['currentBorrowerTd']; + c.pages = map['pages']; + c.createDat = map['createDat']; + c.updateDat = map['updateDat']; + return c; +} + +String formatDate(DateTime date) { + return "${date.day}/${date.month}/${date.year}"; +} + +String formatDuration(Duration duration) { + return "${duration.inDays} days"; +} + +List filterList(List list, bool Function(T) test) { + return list.where(test).toList(); +} + +List mapList(List list, R Function(T) mapper) { + return list.map(mapper).toList(); +} + +T reduceList(List list, T Function(T, T) combine) { + return list.reduce(combine); +} + +void forEachWithIndex(List list, void Function(int, T) action) { + for (int i = 0; i < list.length; i++) { + action(i, list[i]); + } +} + +bool isBookAvailable(Book book) => book.status == BookStatus.available; +String getBookTitle(Book book) => book.title; +int countActiveLoans(List loans) => + loans.where((l) => !l.isReturned).length; +bool isMemberPremium(Member member) => member.tier == MemberTier.premium; + + + + + + + + + + + +// main +void main() async { + LibraryManager manager = LibraryManager(); + bool running = true; + + while (running) { + print("\n========= MAIN MENU ========="); + print("1. Books"); + print("2. Members"); + print("3. Loans"); + print("4. Statistics"); + print("0. Exit"); + + String? choice = readInput("Choose: "); + + switch (choice) { + // ================= BOOKS ================= + case "1": + bool booksMenu = true; + while (booksMenu) { + print("\n--- BOOKS MENU ---"); + print("1. Add Book"); + print("2. View All Books"); + print("3. Available Books"); + print("4. Search"); + print("5. Back"); + + String? bookChoice = readInput("Choose: "); + + switch (bookChoice) { + case "1": + String id = readRequiredInput("Book ID: "); + String title = readRequiredInput("Title: "); + String author = readRequiredInput("Author: "); + Book book = Book(id, title, author); + await manager.addBook(book); + printSuccess("Book added."); + break; + + case "2": + var books = await manager.getAllBooks(); + books.forEach((b) => print(b.getDetails())); + break; + + case "3": + var books = await manager.getAvailableBooks(); + books.forEach((b) => print(b.getDetails())); + break; + + case "4": + String query = readRequiredInput("Search: "); + var results = await manager.searchCatalog(query); + results.forEach((b) => print(b.getDetails())); + break; + + case "5": + booksMenu = false; + break; + + default: + printError("Invalid option."); + } + } + break; + + // ================= MEMBERS ================= + case "2": + bool membersMenu = true; + while (membersMenu) { + print("\n--- MEMBERS MENU ---"); + print("1. Add Member"); + print("2. View All Members"); + print("3. View Premium Members"); + print("4. Back"); + + String? memberChoice = readInput("Choose: "); + + switch (memberChoice) { + case "1": + String id = readRequiredInput("Member ID: "); + String name = readRequiredInput("Name: "); + print("1.Standard 2.Premium"); + String? tierInput = readInput("Choose tier: "); + MemberTier tier = tierInput == "2" + ? MemberTier.premium + : MemberTier.standard; + Member member = Member(id, name, tier); + await manager.addMember(member); + printSuccess("Member added."); + break; + + case "2": + var members = await manager.getAllMembers(); + members.forEach((m) => print(m.getMemberInfo())); + break; + + case "3": + var members = await manager.getPremiumMembers(); + members.forEach((m) => print(m.getMemberInfo())); + break; + + case "4": + membersMenu = false; + break; + + default: + printError("Invalid option."); + } + } + break; + + // ================= LOANS ================= + case "3": + bool loansMenu = true; + while (loansMenu) { + print("\n--- LOANS MENU ---"); + print("1. Loan Book"); + print("2. Return Book"); + print("3. Active Loans"); + print("4. Overdue Loans"); + print("5. Back"); + + String? loanChoice = readInput("Choose: "); + + switch (loanChoice) { + case "1": + String bookId = readRequiredInput("Book ID: "); + String memberId = readRequiredInput("Member ID: "); + try { + await manager.loanBook(bookId, memberId); + printSuccess("Book loaned."); + } catch (e) { + printError(e.toString()); + } + break; + + case "2": + String loanId = readRequiredInput("Loan ID: "); + await manager.returnBook(loanId); + printSuccess("Book returned."); + break; + + case "3": + var loans = await manager.getActiveLoans(); + loans.forEach((l) => print(l.getLoanInfo())); + break; + + case "4": + var loans = await manager.getOverdueLoans(); + loans.forEach((l) => print(l.getLoanInfo())); + break; + + case "5": + loansMenu = false; + break; + + default: + printError("Invalid option."); + } + } + break; + + // ================= STATISTICS ================= + case "4": + print("\n--- STATISTICS ---"); + var bookStats = manager.getBookStatusDistribution(); + bookStats.forEach((k, v) => print("Book $k : $v")); + + var memberStats = manager.getMemberTierDistribution(); + memberStats.forEach((k, v) => print("Member $k : $v")); + break; + + case "0": + running = false; + print("Goodbye ๐Ÿ‘‹"); + break; + + default: + printError("Invalid option."); + } + } + + manager.dispose(); +} diff --git a/mobile/library_management/lib/library_management.dart b/mobile/library_management/lib/library_management.dart new file mode 100644 index 0000000..f64ad72 --- /dev/null +++ b/mobile/library_management/lib/library_management.dart @@ -0,0 +1,3 @@ +int calculate() { + return 6 * 7; +} diff --git a/mobile/library_management/pubspec.lock b/mobile/library_management/pubspec.lock new file mode 100644 index 0000000..5489ca9 --- /dev/null +++ b/mobile/library_management/pubspec.lock @@ -0,0 +1,389 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "796d97d925add7ffcdf5595f33a2066a6e3cee97971e6dbef09b76b7880fd760" + url: "https://pub.dev" + source: hosted + version: "94.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "9c8ebb304d72c0a0c8764344627529d9503fc83d7d73e43ed727dc532f822e4b" + url: "https://pub.dev" + source: hosted + version: "10.0.2" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" + url: "https://pub.dev" + source: hosted + version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + lints: + dependency: "direct dev" + description: + name: lints + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + url: "https://pub.dev" + source: hosted + version: "0.12.18" + meta: + dependency: transitive + description: + name: meta + sha256: "9f29b9bcc8ee287b1a31e0d01be0eae99a930dbffdaecf04b3f3d82a969f296f" + url: "https://pub.dev" + source: hosted + version: "1.18.1" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path: + dependency: "direct main" + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test: + dependency: "direct dev" + description: + name: test + sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a" + url: "https://pub.dev" + source: hosted + version: "1.29.0" + test_api: + dependency: transitive + description: + name: test_api + sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" + url: "https://pub.dev" + source: hosted + version: "0.7.9" + test_core: + dependency: transitive + description: + name: test_core + sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943" + url: "https://pub.dev" + source: hosted + version: "0.6.15" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.dev" + source: hosted + version: "15.0.2" + watcher: + dependency: transitive + description: + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.10.8 <4.0.0" diff --git a/mobile/library_management/pubspec.yaml b/mobile/library_management/pubspec.yaml new file mode 100644 index 0000000..4440e8c --- /dev/null +++ b/mobile/library_management/pubspec.yaml @@ -0,0 +1,15 @@ +name: library_management +description: A sample command-line application. +version: 1.0.0 +# repository: https://github.com/my_org/my_repo + +environment: + sdk: ^3.10.8 + +# Add regular dependencies here. +dependencies: + path: ^1.9.0 + +dev_dependencies: + lints: ^6.0.0 + test: ^1.25.6 diff --git a/mobile/library_management/test/library_management_test.dart b/mobile/library_management/test/library_management_test.dart new file mode 100644 index 0000000..b5c42ff --- /dev/null +++ b/mobile/library_management/test/library_management_test.dart @@ -0,0 +1,8 @@ +import 'package:library_management/library_management.dart'; +import 'package:test/test.dart'; + +void main() { + test('calculate', () { + expect(calculate(), 42); + }); +} diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..c3a09b3 --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,2 @@ +/.env +node_modules \ No newline at end of file diff --git a/web/README .md b/web/README .md new file mode 100644 index 0000000..e13884b --- /dev/null +++ b/web/README .md @@ -0,0 +1,71 @@ +# ๐ŸŒค๏ธ e ยท MDCopy Weather CLI App + +A **Node.js** command-line app that fetches real-time weather data for any city using the **OpenWeatherMap API**. + +--- + +## ๐Ÿ“ Project Structure +``` +project/ +โ”œโ”€โ”€ index.js # Main application logic +โ”œโ”€โ”€ .env # Environment variables (API key) โ€” never share this! +โ”œโ”€โ”€ .gitignore # Ignores .env and node_modules +โ”œโ”€โ”€ package.json # Project metadata & dependencies +โ””โ”€โ”€ README.md # You are here +``` + +--- + +## โš™๏ธ Setup + +1. **Install dependencies** +```bash +npm install +``` + +2. **Get a free API key** +Sign up at [OpenWeatherMap](https://openweathermap.org) โ†’ My Account โ†’ API Keys + +3. **Create your `.env` file** +```env +dotenvOPENWEATHER_API_KEY=your_api_key_here +``` + +โš ๏ธ **Never share your API key or push `.env` to GitHub!** + +--- + +## ๐Ÿš€ Usage +```bash +node index.js +``` + +### Examples +```bash +node index.js London +node index.js Riyadh +node index.js "New York" +``` + +### Example Output +``` +๐Ÿ” Fetching weather for "London"... + + ๐ŸŒ Weather in London, GB + ============================= + ๐ŸŒก๏ธ Temperature : 18ยฐC + Feels like : 16ยฐC + ๐ŸŒค๏ธ Weather : Clear Sky + ๐Ÿ’ง Humidity : 60% + ๐Ÿ’จ Wind Speed : 3.5 m/s + +``` + +--- + +## ๐Ÿง  How It Works + +The app is split into **5 clean functions**: + +| Function | Purpose | +- Uses **ES Modules** โ€” requires `"type": "module"` in `package.json` \ No newline at end of file diff --git a/web/index.js b/web/index.js new file mode 100644 index 0000000..4fd377c --- /dev/null +++ b/web/index.js @@ -0,0 +1,74 @@ +import dotenv from 'dotenv' +dotenv.config(); + +const api = process.env.OPENWEATHER_API_KEY; +const urlBas = process.env.URL_BS; + +function validateApiKey(){ + if(!api){ + console.error('API key is invalid.'); + process.exit(1); + } +} + +function getCity(){ + const city = process.argv[2]; + if(!city){ + console.error("City is invalid."); + console.error('Please provide a city as a command-line argument.'); + process.exit(1) + } + return city; +} + +async function fetchWeather(city){ + const res = await fetch(`${urlBas}?q=${city}&appid=${api}&units=metric`); + const data = await res.json(); + if(!res.ok){ + if(res.status === 401){ + console.error('API key is not valid.'); + }else if(res.status === 404){ + console.error(`city ${city} not found.`); + }else{ + console.error(`Error: ${data.message || 'Unknown error'}`) + } + process.exit(1); + } + return data; +} +function display(data){ + const city = data.name; + const country = data.sys.country; + const temp = Math.round(data.main.temp); + const feelsLike = Math.round(data.main.feels_like); + const weather = data.weather[0].description + .split(" ") + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(" "); + const humidity = data.main.humidity; + const wind = data.wind.speed; + console.log(`Weather in ${city}, ${country}`); + console.log(`Temperature : ${temp}ยฐC (Feels like ${feelsLike}ยฐC)`); + console.log(` Weather : ${weather}`); + console.log(`Humidity : ${humidity}%`); + console.log(`โ”‚ Wind Speed : ${wind} m/s`); +} + +async function main(){ + validateApiKey(); + const city = getCity(); + try{ + console.log(` Fetching weather for ${city}`); + const data = await fetchWeather(city); + display(data); + }catch(e){ + if (e.code === "ENOTFOUND") { + console.error("Network error: Check your internet connection."); + } else { + console.error("Something went wrong:", e.message); + } + process.exit(1); + } +} +main(); + diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 0000000..c7013e7 --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,308 @@ +{ + "name": "project1", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "project1", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^1.13.5", + "dotenv": "^17.3.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..b9bd068 --- /dev/null +++ b/web/package.json @@ -0,0 +1,17 @@ +{ + "name": "project1", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "dependencies": { + "axios": "^1.13.5", + "dotenv": "^17.3.1" + } +}