{"id":14987218,"url":"https://github.com/josephines1/o-present","last_synced_at":"2025-04-11T23:23:29.277Z","repository":{"id":220647096,"uuid":"752193206","full_name":"josephines1/o-present","owner":"josephines1","description":"Sebuah aplikasi open source untuk kebutuhan presensi online berbasis website, dengan fitur GPS dan Foto Selfie.","archived":false,"fork":false,"pushed_at":"2024-08-31T15:38:41.000Z","size":3975,"stargazers_count":28,"open_issues_count":2,"forks_count":8,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-25T19:14:58.183Z","etag":null,"topics":["absensi","absensi-gps","absensi-online","absensi-selfie-base","codeigniter","presensi"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/josephines1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-03T10:00:41.000Z","updated_at":"2025-03-24T05:16:18.000Z","dependencies_parsed_at":"2024-07-07T16:44:38.885Z","dependency_job_id":"bd946d7e-d5c3-41da-8b1a-e6588acee8fa","html_url":"https://github.com/josephines1/o-present","commit_stats":{"total_commits":42,"total_committers":2,"mean_commits":21.0,"dds":"0.23809523809523814","last_synced_commit":"9e5eb991bdb70c67234fb4078220f8251b15588b"},"previous_names":["josephines1/o-present"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josephines1%2Fo-present","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josephines1%2Fo-present/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josephines1%2Fo-present/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josephines1%2Fo-present/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/josephines1","download_url":"https://codeload.github.com/josephines1/o-present/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248493543,"owners_count":21113277,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["absensi","absensi-gps","absensi-online","absensi-selfie-base","codeigniter","presensi"],"created_at":"2024-09-24T14:14:16.229Z","updated_at":"2025-04-11T23:23:29.256Z","avatar_url":"https://github.com/josephines1.png","language":"PHP","funding_links":["https://paypal.me/josephines24"],"categories":[],"sub_categories":[],"readme":"![O-Present](https://github.com/josephines1/o-present/blob/main/public/assets/img/readme/mockup_opresent.png \"O-Present\")\n\n# O-Present\n[![made-with-codeigniter4](https://img.shields.io/badge/Made%20with-CodeIgniter4-DD4814.svg)](https://www.codeigniter.com/) [![Open Source? Yes!](https://badgen.net/badge/Open%20Source%3F/Yes%21/blue?icon=github)](https://github.com/josephines1/o-present)\n\n## Aplikasi Presensi Online Berbasis Web\nPresensi online tanpa ribet! Catat kehadiran dengan cepat menggunakan foto dan GPS. Manajemen presensi yang lebih pintar dan praktis!\n\n## Requirements\n\n- [CodeIgniter 4](https://codeigniter.com/user_guide/intro/index.html)\n- [Composer](https://getcomposer.org/)\n- [XAMPP 8.2.4 or later](https://www.apachefriends.org/download.html)\n- Geolocation-enabled Browser. Read the location access conditions [here](https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features/).\n\n## Features\n\nTemukan fitur-fitur lengkap pada aplikasi presensi O-Present:\n- Presensi Berdasarkan GPS Pegawai\n- Presensi Berdasarkan Foto Selfie\n- Export Laporan Presensi ke dalam Bentuk Microsoft Excel\n- Temukan Data dengan Filter dan Live Search\n- Simpan Data Presensi, Lokasi Presensi, hingga Data Pegawai\n- Sistem Otentikasi (Auth) Multiuser untuk Pegawai, Admin, dan Head\n\n## Getting Started\n\nAnda perlu melakukan sedikit konfigurasi di bawah ini sebelum mulai menjalankan web O-Present:\n1. Anda dapat mengunduh kode sumber aplikasi ini dari repositori GitHub dengan tombol \"Download ZIP\" atau jalankan perintah berikut di terminal Anda:\n   ```console\n   git clone https://github.com/josephines1/o-present.git\n   ```\n\n2. Ekstrak file Zip O-Present yang sudah diunduh dan lokasikan folder aplikasi di dalam folder htdocs.\n\n3. Buka folder project tersebut di Code Editor (seperti Visual Studio Code)\n\n4. Buka terminal, dan pastikan path pada terminal sudah terarah pada directory project website.\n   \n5. Jalankan perintah berikut ini pada terminal untuk memuat package yang dibutuhkan website.\n   ```console\n   composer install\n   ```\n   \n6. Copy file `env` dan beri nama file duplikatnya menjadi `.env`\n   - Pertama, ubah konfigurasi CI_ENVIROMENT menjadi seperti di bawah ini.\n     ```\n      CI_ENVIRONMENT = development\n      ```\n     \n   - Lalu, konfigurasikan url utama untuk web Anda.\n     ```\n      app.baseURL = 'http://localhost:8080/'\n      ```\n     \n   - Kemudian, konfirgurasikan database. Sesuaikan dengan database milik Anda.\n     ```\n      database.default.hostname = localhost\n      database.default.database = o-present\n      database.default.username = root\n      database.default.password = \n      database.default.DBDriver = MySQLi\n      database.default.DBPrefix =\n      database.default.port = 3306\n      ```\n\n7. Buka file `RoleFilter.php` dalam folder `vendor\\myth\\auth\\src\\Filters\\RoleFilter.php`.\n   \n8. Modifikasi function before menjadi seperti berikut ini.\n   ```\n   public function before(RequestInterface $request, $arguments = null)\n    {\n        // If no user is logged in then send them to the login form.\n        if (! $this-\u003eauthenticate-\u003echeck()) {\n            session()-\u003eset('redirect_url', current_url());\n\n            return redirect($this-\u003ereservedRoutes['login']);\n        }\n\n        if (empty($arguments)) {\n            return;\n        }\n\n        // Check each requested permission\n        foreach ($arguments as $group) {\n            if ($this-\u003eauthorize-\u003einGroup($group, $this-\u003eauthenticate-\u003eid())) {\n                return;\n            }\n        }\n\n        // Check if the user has the 'head' role\n        if ($this-\u003eauthorize-\u003einGroup('head', $this-\u003eauthenticate-\u003eid())) {\n            // Check each requested permission for 'head'\n            if (!empty($arguments)) {\n                foreach ($arguments as $group) {\n                    if ($this-\u003eauthorize-\u003einGroup($group, $this-\u003eauthenticate-\u003eid())) {\n                        return;\n                    }\n                }\n\n                // If 'head' does not have the required permission, redirect to '/admin'\n                return redirect()-\u003eto('/admin')-\u003ewith('error', lang('Auth.notEnoughPrivilege'));\n            }\n        }\n\n        if ($this-\u003eauthenticate-\u003esilent()) {\n            $redirectURL = session('redirect_url') ?? route_to($this-\u003elandingRoute);\n            unset($_SESSION['redirect_url']);\n\n            return redirect()-\u003eto($redirectURL)-\u003ewith('error', lang('Auth.notEnoughPrivilege'));\n        }\n\n        // throw new PermissionException(lang('Auth.notEnoughPrivilege'));\n        return redirect()-\u003eto('/');\n    }\n   ```\n\n9. Buka file `AuthController.php` dalam folder `vendor\\myth\\auth\\src\\Controllers\\AuthController.php`.\n   - Modifikasi function `attemptForgot` menjadi seperti berikut ini.\n     ```\n     public function attemptForgot($email = false)\n     {\n        if ($this-\u003econfig-\u003eactiveResetter === null) {\n            return redirect()-\u003eroute('login')-\u003ewith('error', lang('Auth.forgotDisabled'));\n        }\n\n        if (!$email) {\n            $rules = [\n                'email' =\u003e [\n                    'label' =\u003e lang('Auth.emailAddress'),\n                    'rules' =\u003e 'required|valid_email',\n                ],\n            ];\n\n            if (!$this-\u003evalidate($rules)) {\n                return redirect()-\u003eback()-\u003ewithInput()-\u003ewith('errors', $this-\u003evalidator-\u003egetErrors());\n            }\n        }\n\n        $users = model(UserModel::class);\n\n        if ($email) {\n            $user = $users-\u003ewhere('email', $email)-\u003efirst();\n        } else {\n            $user = $users-\u003ewhere('email', $this-\u003erequest-\u003egetPost('email'))-\u003efirst();\n        }\n\n        if (null === $user) {\n            return redirect()-\u003eback()-\u003ewith('error', lang('Auth.forgotNoUser'));\n        }\n\n        // Save the reset hash /\n        $user-\u003egenerateResetHash();\n        $users-\u003esave($user);\n\n        $resetter = service('resetter');\n        $sent     = $resetter-\u003esend($user);\n\n        if (!$sent) {\n            return redirect()-\u003eback()-\u003ewithInput()-\u003ewith('error', $resetter-\u003eerror() ?? lang('Auth.unknownError'));\n        }\n\n        return redirect()-\u003eroute('reset-password')-\u003ewith('message', lang('Auth.forgotEmailSent'));\n     }\n     ```\n\n   - Modifikasi function `resendActivateAccount` menjadi seperti berikut ini.\n     ```\n     public function resendActivateAccount($login = false)\n     {\n        if ($this-\u003econfig-\u003erequireActivation === null) {\n            return redirect()-\u003eroute('login');\n        }\n\n        $throttler = service('throttler');\n\n        if ($login == false) {\n            if ($throttler-\u003echeck(md5($this-\u003erequest-\u003egetIPAddress()), 2, MINUTE) === false) {\n                return service('response')-\u003esetStatusCode(429)-\u003esetBody(lang('Auth.tooManyRequests', [$throttler-\u003egetTokentime()]));\n            }\n            $login = urldecode($this-\u003erequest-\u003egetGet('login'));\n        }\n        $type  = filter_var($login, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';\n\n        $users = model(UserModel::class);\n\n        $user = $users-\u003ewhere($type, $login)\n            -\u003ewhere('active', 0)\n            -\u003efirst();\n\n        if (null === $user) {\n            return redirect()-\u003eroute('login')-\u003ewith('error', lang('Auth.activationNoUser'));\n        }\n\n        $activator = service('activator');\n        $sent      = $activator-\u003esend($user);\n\n        if (!$sent) {\n            return redirect()-\u003eback()-\u003ewithInput()-\u003ewith('error', $activator-\u003eerror() ?? lang('Auth.unknownError'));\n        }\n\n        // Success!\n        // return redirect()-\u003eroute('login')-\u003ewith('message', lang('Auth.activationSuccess'));\n        return redirect()-\u003eto('/data-pegawai')-\u003ewith('berhasil', 'Email aktivasi berhasil terkirim');\n     }\n     ```\n     \n10. Buka file `Auth.php` dalam folder `vendor\\myth\\auth\\src\\Config\\Auth.php`.\n    - Konfigurasikan defaultUserGroup.\n      ```\n      public $defaultUserGroup = ['pegawai'];\n      ```\n      \n    - Konfigurasikan tampilan auth website.\n      ```\n      public $views = [\n        'login'           =\u003e 'App\\Views\\auth\\login',\n        'register'        =\u003e 'Myth\\Auth\\Views\\register',\n        'forgot'          =\u003e 'App\\Views\\auth\\forgot',\n        'reset'           =\u003e 'App\\Views\\auth\\reset-password',\n        'emailForgot'     =\u003e 'Myth\\Auth\\Views\\emails\\forgot',\n        'emailActivation' =\u003e 'Myth\\Auth\\Views\\emails\\activation',\n      ];\n      ```\n    \n    - Konfigurasikan agar tidak tampil halaman register mandiri\n      ```\n      public $allowRegistration = null;\n      ```\n      \n11. Buka file `Email.php` dalam folder `app/Config/Email.php`.\n    \n12. Isi fromName dan fromEmail untuk digunakan saat mengirim email untuk reset password, dan sebagainya.\n    ```\n    public string $fromEmail  = 'your email here';\n    public string $fromName   = 'O-Present';\n    ```\n13. Isi nilai SMTPUser dengan email yang sama dengan yang Anda masukkan di $fromEmail.\n    ```\n    public string $SMTPUser = 'your email here';\n    ```\n    \n14. Isi nilai SMTPPass dengan kode yang Anda dapatkan dari verifikasi dua langkah pada Akun Google Anda untuk Aplikasi XAMPP. Untuk cara lengkap memperoleh kode verifikasi 2 langkah Akun Google dapat dilihat pada link ini https://support.google.com/accounts/answer/185833?hl=en.\n    ```\n    public string $SMTPPass = 'your code here';\n    ``` \n    \n15. Aktifkan server Apache dan MySQL di XAMPP Control Panel Anda untuk memulai server pengembangan lokal.\n    \n16. Kunjungi `localhost/phpmyadmin` pada browser Anda, lalu buat database baru dengan nama o-present atau sesuaikan dengan nama database yang Anda inginkan\n\n17. Kembali ke terminal, jalankan perintah migrate dan seed\n    - Migrate\n      ```console\n      php spark migrate -2024-02-02-091537_create_opresent_tables\n      php spark migrate -2024-02-02-142048_create_auth_tables\n      ```\n\n    - Seed\n      ```console\n      php spark db:seed JabatanSeeder\n      php spark db:seed LokasiSeeder\n      php spark db:seed PegawaiSeeder\n      php spark db:seed UsersSeeder\n      php spark db:seed AuthGroupsSeeder\n      php spark db:seed AuthPermissionsSeeder\n      php spark db:seed AuthGroupsPermissionsSeeder\n      php spark db:seed AuthGroupsUsersSeeder\n      ```\n\n18. Selanjutnya, start server dengan menjalankan perintah berikut ini di terminal.\n    ```console\n    php spark serve\n    ```\n      \n19. Selesai! Anda dapat mengakses web melalui port 8080 `http://localhost:8080` di server lokal.\n\n## First Usage\n\nSetelah melakukan instalasi dan konfigurasi O-Present, Anda dapat melakukan login pada aplikasi dengan email dan password sebagai berikut.\n\n### Head\n```\nEmail: jaya@present.com\nPassword: 123456\n```\n\n### Admin\n```\nEmail: tamani@present.com\nPassword: 123456\n```\n\n### Pegawai\n```\nEmail: choland@present.com\nPassword: 123456\n```\n\nSetelah berhasil melakukan login, Anda dapat mencoba untuk menambahkan lokasi presensi baru untuk mencoba melakukan presensi masuk dan presensi keluar.\n\n## Services\n\nLayanan di bawah ini tersedia pada aplikasi O-Present.\n\n#### Presensi Masuk \u0026 Keluar\nPada role Admin dan Pegawai, pengguna dapat melakukan presensi masuk pada lokasi presensi yang sudah diatur. Untuk layanan ini, akses kamera dan lokasi diperlukan untuk melakukan presensi masuk dan keluar. Foto presensi yang sudah ter-capture, akan disimpan dalam folder public\\assets\\img\\foto_presensi.\n\n#### Rekap Presensi\nPada role Admin dan Pegawai, pengguna dapat melihat rekap presensi yang sudah tercatat dalam database, serta mengunduhnya dalam bentuk Microsoft Excel.\n\n#### Laporan Presensi Harian dan Bulanan\nPada role Head dan Admin, pengguna dapat melihat laporan presensi harian dan bulanan milik seluruh pegawai yang sudah tercatat dalam database, serta mengunduhnya dalam bentuk Microsoft Excel.\n\n#### Pengajuan Ketidakhadiran\nPada role Admin dan Pegawai, pengguna dapat mengajukan ketidakhadiran dengan batas H-3 dan durasi ketidakhadiran selama 3 bulan. Pengguna diwajibkan menggunggah file surat keterangan tidak hadir dalam bentuk PDF. File yang sudah terunggah disimpan dalam folder public\\assets\\file\\surat_keterangan_ketidakhadiran. Pengguna dapat mengunduh daftar ketidakhadirannya dalam satu bulan ke dalam bentuk Microsoft Excel.\n\nDetail tipe ketidakhadiran adalah sebagai berikut\n**Cuti**\n- Pengajuan: Dapat diajukan beberapa hari sebelumnya sesuai kebijakan perusahaan, yang diatur di rules `daysAfter[]` di file `app\\Controllers\\Ketidakhadiran.php`.\n- Status Approval: Memerlukan persetujuan dari atasan. Tidak ada persetujuan otomatis.\n\n**Izin**\n- Pengajuan: Dapat diajukan pada hari yang sama atau untuk hari mendatang, tetapi tidak dapat untuk tanggal kemarin atau sebelumnya.\n- Status Approval: Memerlukan persetujuan dari atasan. Tidak ada persetujuan otomatis.\n\n**Sakit**\n- Pengajuan: Hanya dapat diajukan pada hari ini atau hari esok. Tidak bisa untuk tanggal lebih dari hari esok.\n- Status Approval: Otomatis disetujui tanpa perlu persetujuan dari atasan.\n\n#### Kelola Ketidakhadiran\nPada role Head, pengguna dapat mengelola ijin ketidakhadiran, yang meliputi PENDING, APPROVED, dan REJECTED. Pengguna dapat mengunduh daftar ketidakhadiran dari seluruh pegawai ke dalam bentuk Microsoft Excel.\n\nPada website, batas pengajuan ketidakhadiran dapat dilakukan minimal 3 hari sebelum tanggal mulai cuti untuk pengguna dengan role Head menentukan status pengajuan kehadiran. Untuk memodifikasi batas pengajuan, dapat dilakukan pada rules `daysAfter[]` di file `app\\Controllers\\Ketidakhadiran.php` pada validasi tanggal mulai (baris ke 287 dan 385) menjadi seperti ini:\n\n```\n'tanggal_mulai' =\u003e [\n                'rules' =\u003e 'required|valid_date[Y-m-d]|daysAfter[3]',\n                'errors' =\u003e [\n                    'required' =\u003e 'Tanggal mulai ketidakhadiran wajib diisi.',\n                    'valid_date' =\u003e 'Tanggal harus dalam format YYYY-MM-DD.',\n                    'daysAfter' =\u003e 'Pengajuan cuti harus minimal 3 hari sebelum tanggal cuti yang diinginkan.'\n                ]\n            ],\n```\n\n#### Master Data\nPada role Head dan Admin, pengguna dapat mengelola data jabatan, lokasi presensi, dan pegawai. Pengguna dapat menemukan data-data tersebut dengan memanfaatkan fitur filter data dan live search data sehingga data dapat ditemukan dengan cepat dan efisien. Pengguna juga dapat mengunduh data-data tersebut ke dalam bentuk Microsoft Excel.\n\nUntuk data beserta akun pegawai yang baru ditambahkan, pegawai dapat mengakses website setelah melewati proses aktivasi, dengan password default '123456'. Pengguna dengan role Head dan Admin dapat menambahkan akun baru untuk pegawai dan menentukan langsung status aktivasi nya (Aktivasi Instan, Aktivasi Melalui Email, atau Aktivasi Nanti).\n\n#### Kelola Profile\nUntuk semua role, pengguna dapat mengelola profile nya, yang meliputi ubah foto profile, ubah username, ubah nama dan info profile lainnya.\n\n#### Lupa \u0026 Ubah Password\nUntuk pengguna yang lupa password, pengguna dapat memilih opsi \"Lupa Password\" yang tersedia pada halaman Login. Untuk pengguna yang ingin melakukan perubahan password, pengguna dapat memilih opsi \"Reset Password\" yang tersedia pada halaman profile. Sistem akan mengirimkan email kepada pengguna untuk instruksi reset password.\n\n#### Ubah Email\nUntuk pengguna yang ingin melakukan perubahan email, pengguna dapat memilih opsi \"Ubah Email\" yang tersedia pada halaman profile. Sistem akan mengirimkan token ke alamat email pengguna saat ini, untuk instruksi selanjutnya dalam melakukan perubahan alamat email.\n\n## Database\nBerikut ini adalah struktur table database untuk aplikasi O-Present.\n![O-Present Database](https://github.com/josephines1/o-present/blob/main/public/assets/img/readme/db_opresent.png \"O-Present\")\n\n## Tech\n\nTeknologi dalam aplikasi ini:\n- [CodeIgniter 4](https://www.codeigniter.com/) - a flexible application development framework.\n- [Myth/Auth](https://github.com/lonnieezell/myth-auth) - a flexible, Powerful, Secure auth package for CodeIgniter 4.\n- [Tabler.io](https://tabler.io/) - a free and open source web application UI kit based on Bootstrap 5.\n- [jQuery](https://jquery.com/) - a fast, small, and feature-rich JavaScript library.\n- [WebcamJS](https://pixlcore.com/read/WebcamJS) - a small standalone JavaScript library for capturing still images\n- [Leaflet](https://leafletjs.com/) - a JavaScript library used to build web mapping applications.\n\n## Tips \u0026 Troubleshooting\nJika Anda mendapat pesan error saat menjalankan source code O-Present, silahkan melihat solusi-solusi di bawah ini.\n\n1. **Mengatasi Error \"Only secure origins are allowed\"**\n   Jika Anda ingin mencoba mengakses web O-Present di device yang berbeda dengan device server menggunakan IP Address dan Port, kemungkinan akan mengalami error ini karena masalah izin akses lokasi menggunakan HTTP. Anda bisa menggunakan ngrok untuk membuat tunneling ke HTTPS.\n\n   **Solusi:** Jalankan `ngrok http [port]` untuk mengakses aplikasi Anda melalui HTTPS.\n\n   Solusi dari @ikii378.\n\n2. **Mengatasi Error \"Origin Does Not Have Permission to use Geolocation Service\" pada iOS**\n   Pengguna iOS mungkin mengalami bug di mana mereka tidak bisa menggunakan layanan geolocation dan menerima pesan error \"Origin Does Not Have Permission to use Geolocation Service\" saat mencoba melakukan absen.\n\n   **Solusi:** Untuk mengatasi masalah ini, tambahkan header berikut pada konfigurasi web server NGINX:\n\n   ```nginx\n   add_header Content-Security-Policy \"upgrade-insecure-requests\";\n   ```\n\n   Solusi dari @mrandrian ([GitHub Profile](https://github.com/mrandrian)).\n\n## Contribution\n\nKontribusi untuk penyempurnaan aplikasi ini sangat dihargai. Jika Anda menemukan masalah atau bug, silahkan membuat issue baru dalam repositori ini.\n\n## Support\n\n[![PayPal](https://img.shields.io/badge/PayPal-00457C?style=for-the-badge\u0026logo=paypal\u0026logoColor=white)](https://paypal.me/josephines24)\n[![KaryaKarsa](https://image.typedream.com/cdn-cgi/image/width=120,format=auto,fit=scale-down,quality=100/https://api.typedream.com/v0/document/public/07480db4-7b4e-4309-9be2-b4e218db150e/2IGRM5CUZESdabtjezsFTWnWFVR_karyakarsa-logo-white.png?bucket=document)](https://karyakarsa.com/josephines24)\n\n## Credits\n\n\u003e Made by [Josephine](https://josephines1.github.io/).\n\u003e Template by [tabler.io](tabler.io)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosephines1%2Fo-present","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjosephines1%2Fo-present","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosephines1%2Fo-present/lists"}