diff --git a/week_5/activity-tracker/Gemfile b/week_5/activity-tracker/Gemfile index 6a97ca54..7f78e313 100644 --- a/week_5/activity-tracker/Gemfile +++ b/week_5/activity-tracker/Gemfile @@ -46,7 +46,7 @@ gem "bootsnap", require: false # gem "sassc-rails" # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] -# gem "image_processing", "~> 1.2" +gem "image_processing", "~> 1.2" group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem @@ -75,4 +75,6 @@ gem "rails-controller-testing" gem 'bootstrap', '~> 5.2.2' -gem 'devise' \ No newline at end of file +gem 'prawn', '~> 2.4' + +gem 'devise' diff --git a/week_5/activity-tracker/Gemfile.lock b/week_5/activity-tracker/Gemfile.lock index bc7790d9..724d0351 100644 --- a/week_5/activity-tracker/Gemfile.lock +++ b/week_5/activity-tracker/Gemfile.lock @@ -107,6 +107,9 @@ GEM activesupport (>= 5.0) i18n (1.12.0) concurrent-ruby (~> 1.0) + image_processing (1.12.2) + mini_magick (>= 4.9.5, < 5) + ruby-vips (>= 2.0.17, < 3) importmap-rails (1.1.5) actionpack (>= 6.0.0) railties (>= 6.0.0) @@ -127,6 +130,7 @@ GEM marcel (1.0.2) matrix (0.4.2) method_source (1.0.0) + mini_magick (4.12.0) mini_mime (1.1.2) minitest (5.17.0) msgpack (1.6.0) @@ -143,7 +147,11 @@ GEM nokogiri (1.14.0-x86_64-linux) racc (~> 1.4) orm_adapter (0.5.0) + pdf-core (0.9.0) popper_js (2.11.6) + prawn (2.4.0) + pdf-core (~> 0.9.0) + ttfunk (~> 1.7) public_suffix (5.0.1) puma (5.6.5) nio4r (~> 2.0) @@ -189,6 +197,8 @@ GEM actionpack (>= 5.2) railties (>= 5.2) rexml (3.2.5) + ruby-vips (2.1.4) + ffi (~> 1.12) rubyzip (2.3.2) sassc (2.4.0) ffi (~> 1.9) @@ -215,6 +225,7 @@ GEM thor (1.2.1) tilt (2.0.11) timeout (0.3.1) + ttfunk (1.7.0) turbo-rails (1.3.2) actionpack (>= 6.0.0) activejob (>= 6.0.0) @@ -249,8 +260,10 @@ DEPENDENCIES capybara debug devise + image_processing (~> 1.2) importmap-rails jbuilder + prawn (~> 2.4) puma (~> 5.0) rails (~> 7.0.4) rails-controller-testing diff --git a/week_5/activity-tracker/app/assets/stylesheets/light-mode.css b/week_5/activity-tracker/app/assets/stylesheets/light-mode.css new file mode 100644 index 00000000..79c6f209 --- /dev/null +++ b/week_5/activity-tracker/app/assets/stylesheets/light-mode.css @@ -0,0 +1,911 @@ +:root { + /* Colors */ + --primary: #49c195; + --secondary: #5e5e5e; + --success: #28a745; + --info: #17a2b8; + --warning: #ffc107; + --danger: #dc3545; + --focus: #007bff; + --alternate: #712cf9; + + /* Borders */ + --primary-border: #349672; + --secondary-border: #d3d3d3; + --success-border: #28a745; + --info-border: #17a2b8; + --warning-border: #ffc107; + --danger-border: #dc3545; + --focus-border: #007bff; + --alternate-border: #8f33b1; + + /* Text */ + --text: #212529; + --text-disabled: #6c757d; + --placeholder-text: #6c757d; + --primary-text: #bf27a3; + --secondary-text:white; + --success-text: #28a745; + --info-text: #17a2b8; + --warning-text: #ca370f; + --danger-text: #14c422; + --focus-text: #007bff; + --alternate-text: #e3872b; + + /* Table */ + --table-border: #dee2e6; + --table-hover: #f8f9fa; + + /* Other */ + --switchery-background: #dee2e6; + --loader-img: #212529; + --form-control-bg: #f8f9fa; + --form-control-border: #ced4da; + --primary-checkbox: #0077cc; + --toggle-light-mode-text: #212529; + --profile-update-detail: #0077cc; +} + +/* Placeholder for input */ +::-webkit-input-placeholder { + /* WebKit, Blink, Edge */ + color: var(--placeholder-text) !important; +} + +:-moz-placeholder { + /* Mozilla Firefox 4 to 18 */ + color: var(--placeholder-text) !important; + opacity: 1; +} + +::-moz-placeholder { + /* Mozilla Firefox 19+ */ + color: var(--placeholder-text) !important; + opacity: 1; +} + +:-ms-input-placeholder { + /* Internet Explorer 10-11 */ + color: var(--placeholder-text) !important; +} + +::-ms-input-placeholder { + /* Microsoft Edge */ + color: var(--placeholder-text) !important; +} + +::placeholder { + /* Most modern browsers support this now. */ + color: var(--placeholder-text) !important; +} + +input { + color: var(--text) !important; + background: var(--form-control-bg); + border: 1px solid var(--form-control-border); +} + +/* Text color */ +body { + color: var(--text); + background:white; +} + +a { + /* TODO: Change link colors */ + color: white !important; +} + +.text-primary { + color: var(--primary-text) !important; +} + +.text-secondary { + color: var(--secondary-text) !important; +} + +.text-alternate { + color: var(--alternate-text) !important; +} + +/* Top Bar */ +.bg-royal { + background-image: linear-gradient(to right, #1b1b1b, #000000) !important; +} + +/* Title-Nav Bar */ +.app-theme-gray .app-header { + background: #030607; + -webkit-box-shadow: -1px 14px 44px -4px rgba(0, 0, 0, 0.65); + -moz-box-shadow: -1px 14px 44px -4px rgba(0, 0, 0, 0.65); + box-shadow: -1px 14px 44px -4px rgba(0, 0, 0, 0.65); +} + +.horizontal-nav-menu>li>a span::before { + background: var(--primary); +} + +.app-theme-gray .app-page-title { + border-bottom: none; +} + +.header-mobile-open { + background: #1a1a1a; + border-color: #151515; +} + +/* IRIS LOGO */ +.app-header__logo { + background: var(--primary); +} + +/* Page Title -- Bread crumb Bar */ +.app-theme-gray .app-page-title { + background: #1a1a1a; +} + +.app-page-title .page-title-wrapper::before { + background: var(--primary); +} + +/* show/hide menu bar */ +.app-theme-gray .app-inner-bar { + background: #262626; + border-bottom: none; +} + +/* menu */ +.dropdown-menu { + background-color: #262626; +} + +.header-mobile-open .horizontal-nav-menu { + background-color: #212121; +} + +.header-mobile-open .horizontal-nav-menu>li { + border-color: #1f1f1f; +} + +/* side bar color */ +.app-inner-layout__sidebar { + background: #1e1e1e !important; + color: white; +} + +.dropdown-item { + color: white; +} + +.dropdown-item:hover, +.dropdown-item:focus { + color: #fff; + background: var(--primary-border); +} + +.dropdown-item.active, +.dropdown-item:active { + color: #fff; + text-decoration: none; + background-color: var(--primary); +} + +.app-inner-layout.app-inner-layout-page .app-inner-layout__wrapper .app-inner-layout__sidebar { + border-right: none; +} + +.app-main__inner { + background: #1a1a1a; +} + +/* content body */ +.app-inner-layout__wrapper { + background: #1a1a1a; +} + +/* HEADER */ + +.header-btn-lg::before { + background: #474747; +} + +.grid-menu [class*="col-"] { + border-right: #474747 solid 0; + border-bottom: #474747 solid 1px; +} + +.nav-item.nav-item-header { + color: var(--secondary-text); +} + +.divider { + background: #474747; +} + +/* When scrolling up to the top, a small bar or 2 appears. Color of bar is this */ +.app-theme-gray.app-container { + background: rgb(26, 26, 26); +} + +/* FOOTER */ +.app-wrapper-footer .app-footer { + border: none; +} + +.app-theme-gray .app-footer { + background: #131313; +} + +.footer-dots .dots-separator { + background: #474747; +} + +.list-group-item { + background-color: transparent; +} + +.border-light { + border-color: #2a2a2a !important; +} + +.widget-numbers { + color: var(--text); +} + +/* FORMS */ + +select { + background-color: #f5f5f5; + border-color: #ddd; + color: #333; +} + +.form-control { + background: #f5f5f5; + border-color: #ddd; + color: #333; +} + +.form-control:focus { + background-color: #eee; + border-color: #66afe9; + color: #333; +} + +.form-control:disabled, +.form-control[readonly] { + background-color: #292929 !important; +} + +.input-group-text { + background-color: #f5f5f5; + border: 1px solid #ddd; + color: #333; +} + +.custom-select { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} + +.custom-select:focus { + border-color: #66afe9; + box-shadow: 0 0 0 0.2rem #ccebff; +} + +.custom-control-label::before { + background-color: #f5f5f5; + border-color: #ddd; +} + +.custom-control-input:checked~.custom-control-label::before { + background-color: #66afe9; + border-color: #66afe9; +} + +/* Progress Bar */ + +.progress { + background-color: transparent; +} + +.progress-bar { + background-color: var(--primary); +} + +/* Select2 */ + +.select2-container--bootstrap4 .select2-selection { + background-color: var(--form-control-bg); + border: 1px solid #1a1a1a; + color: white !important; +} + +.select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered { + color: white; + padding: 0; +} + +.select2-container--open .select2-dropdown { + color: #fff; + background-color: #262626; +} + +.select2-container--bootstrap4 .select2-results__option[aria-selected=true] { + background-color: var(--primary); + color: #fff; +} + +.select2-container--bootstrap4 .select2-results__option--highlighted[aria-selected] { + background-color: var(--primary); +} + +.select2-container--bootstrap4 .select2-search--dropdown .select2-search__field { + background-color: var(--form-control-bg); + border-color: #1a1a1a; + color: #fff; +} + +.select2-container--bootstrap4 .select2-selection--multiple .select2-selection__choice { + background-color: #333131; + border-color: #1a1a1a; + color: #fff; +} + +.select2-container--bootstrap4.select2-container--focus .select2-selection, +.select2-container--bootstrap4.select2-container--open .select2-selection { + border-color: #1a1a1a; +} + +/* Daterangepicker */ + +.daterangepicker { + color: var(--text); +} + +.daterangepicker .calendar-table { + background-color: #262626; + border: #232323; +} + +.daterangepicker td.off, +.daterangepicker td.off.in-range, +.daterangepicker td.off.start-date, +.daterangepicker td.off.end-date { + background-color: #202020; + color: var(--text-disabled); +} + +.daterangepicker td.active, +.daterangepicker td.active:hover { + background-color: var(--primary); +} + +.daterangepicker td.available:hover, +.daterangepicker th.available:hover { + background-color: var(--primary); +} + +.daterangepicker select.hourselect, +.daterangepicker select.minuteselect, +.daterangepicker select.secondselect, +.daterangepicker select.ampmselect { + background-color: var(--form-control-bg); + border-color: var(--form-control-border); +} + +/* FullCalendar */ + +.fc-state-active { + background-color: #464646 !important; + color: #fff !important; +} + +/* Tooltip */ + +.popover-body { + background: #262626; + color: #fff; +} + +.bs-popover-right .arrow::after, +.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #262626; +} + +.ui-widget.ui-widget-content { + border-color: #232323; +} + +.ui-widget-content { + border-color: #232323; + background: #262626; + color: var(--text); +} + +.ui-widget-shadow { + -webkit-box-shadow: 0px 0px 5px #222; + box-shadow: 0px 0px 5px #222; +} + +/* CARD STYLES */ +.card { + background-color: #f7f7f7; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); +} + +.card-body { + background: #fff; + color: #333; + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0.25rem; +} + +.card-deck { + display: flex; + flex-direction: column; +} + +.card-deck .card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-deck { + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; + } + .card-deck .card { + display: flex; + flex: 1 0 0%; + flex-direction: column; + margin-right: 15px; + margin-bottom: 15px; + margin-left: 15px; + } +} + +.ui-widget-header { + border: 1px solid #ccc; + background: #f7f7f7; + color: #333; + font-weight: bold; +} + +.card-header { + background: #f7f7f7 !important; + color: #333; +} + +.card-header>.nav .nav-link.active { + color: var(--primary); +} + +.card-footer { + background: #f7f7f7 !important; + color: #333; +} + +.card-title { + color: rgb(0, 0, 0); +} + +.card-subtitle { + color: var(--secondary-text); +} + +.card:hover { + transform: scale(1.015); + transition: all 0.25s ease; + } + + +/* MODAL STYLES */ + +.modal-body { + background: #2d2d2d; + color: white; +} + +.modal-content { + background-color: #2d2d2d; + border: none; +} + +.modal-header { + background: #262626 !important; + border-bottom: 1px solid #1f1f1f; + color: white; +} + +.modal-footer { + background: #262626 !important; + border-top: 1px solid #1f1f1f; + color: white; +} + +.modal-dialog { + box-shadow: 0 0.76875rem 2.4875rem rgb(20 20 20 / 30%), 0 1.3375rem 1.70625rem rgb(20 20 20 / 30%), 0 0.55rem 0.53125rem rgb(0 0 0 / 5%), 0 0.225rem 0.4375rem rgb(20 20 20 / 30%); + border-radius: 0.25rem; +} + +.card-title { + color: rgb(22, 22, 22); + text-align: center; +} + +.card-subtitle { + color: rgb(111, 118, 219); +} + + +/* BUTTON STYLES */ + +.btn { + color: white; +} + +.btn-light { + background-color: #2f2f2f; + border-color: #242424; +} + +.btn-light.btn-shadow:hover { + box-shadow: 0 0.125rem 0.625rem #262626, 0 0.0625rem 0.125rem #262626; +} + +.btn-light:hover, +.btn-light:focus, +.btn-light:active { + background-color: #363636; + border-color: #1c1c1c; + color: #fff; +} + +.btn-light.btn-shadow { + box-shadow: 0 0.125rem 0.625rem #262626, 0 0.0625rem 0.125rem #262626; +} + +.btn-primary { + background-color: var(--primary); + border-color: var(--primary-border); +} + +.btn-primary:hover { + background-color: var(--primary-border); + border-color: var(--primary); +} + +.btn-secondary { + background-color: var(--secondary); + border-color: var(--secondary-border); +} + +.btn-success { + background-color: var(--success); + border-color: var(--success-border); +} + +.btn-info { + background-color: var(--info); + border-color: var(--info-border); +} + +.btn-warning { + background-color: var(--warning); + border-color: var(--warning-border); +} + +.btn-danger { + background-color: var(--danger); + border-color: var(--danger-border); +} + +.btn-focus { + background-color: var(--focus); + border-color: var(--focus-border); +} + +.btn-alternate { + background-color: var(--alternate); + border-color: var(--alternate-border); +} + +.btn-new { + background-color: #712cf9; + +} + +.btn-outline-primary { + border-color: var(--primary-border); +} + +.btn-outline-secondary { + border-color: var(--secondary-border); +} + +.btn-outline-success { + border-color: var(--success-border); +} + +.btn-outline-info { + border-color: var(--info-border); +} + +.btn-outline-warning { + border-color: var(--warning-border); +} + +.btn-outline-danger { + border-color: var(--danger-border); +} + + + +/* Bootstrap Alerts */ + +.alert { + color: #ffffff !important; +} + +.alert-primary { + background-color: var(--primary); + border-color: var(--primary-border); +} + +.alert-success { + background-color: var(--success); + border-color: var(--success-border); +} + +.alert-info { + background-color: var(--info); + border-color: var(--info-border); +} + +.alert-warning { + background-color: var(--warning); + border-color: var(--warning-border); +} + +.alert-danger { + color: #fff; + background-color: var(--danger); + border-color: var(--danger-border); +} + +.close { + color: #fff; +} + +.close:hover { + color: inherit; +} + +/* bootstrap backgrounds */ + + +.bg-primary { + background-color: var(--primary) !important; +} + +.bg-secondary { + background-color: var(--secondary) !important; +} + +.bg-success { + background-color: var(--success) !important; +} + +.bg-info { + background-color: var(--info) !important; +} + +.bg-warning { + background-color: var(--warning) !important; +} + +.bg-danger { + background-color: var(--danger) !important; +} + +.bg-focus { + background-color: var(--focus) !important; +} + +.bg-alternate { + background-color: var(--alternate) !important; +} + +/* Bootstrap Badges */ + +.badge-primary { + background-color: var(--primary); +} + +.badge-secondary { + background-color: var(--secondary); +} + +.badge-info { + background-color: var(--info); +} + +.badge-success { + background-color: var(--success); +} + +.badge-warning { + background-color: var(--warning); +} + +.badge-danger { + background-color: var(--danger); +} + +/* TABLE */ + +tr { + border-color: var(--table-border); +} + +.table thead th { + vertical-align: bottom; + border-color: var(--table-border); + background: #1f1f1f; +} + +.table tbody tr { + background: #242424; +} + +.table-bordered th, +.table-bordered td { + border-color: var(--table-border); +} + +.table-striped tbody tr:nth-child(odd) { + background-color: #212121 !important; +} + +.table { + border-color: var(--table-border); +} + +.table-hover tbody tr:hover { + background-color: var(--table-hover) !important; +} + +.table-primary { + background-color: var(--primary); +} + +.table-primary:hover { + background-color: var(--primary-border) !important; +} + +.table th, +.table td { + border-color: var(--table-border); +} + +/* Datatable */ + +/* pagination buttons (selected) */ +.pagination li a { + color: var(--primary); + background-color: #383838; + border: 1px solid #262626; +} + +/*other pagination buttons*/ +.page-item.disabled .page-link, +.pagination .disabled.page-number .page-link { + color: var(--text-disabled) !important; + background-color: #383838; + border-color: #262626; +} + +.pagination li a:hover { + background-color: #555555; + border-color: #262626; +} + +.pagination li.disabled a { + background-color: #383838; + border-color: #262626; +} + +.page-item.active .page-link, +.pagination .active.page-number .page-link { + background-color: var(--primary); + border-color: var(--primary-border); +} + +/* Copy button */ +div.dt-button-info { + background-color: #212121 !important; + border-color: #111 !important; + box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.3); +} + +div.dt-button-info h2 { + border-color: #1a1a1a !important; + background-color: #262626 !important; +} + +/* Dashboard Buttons */ + +/* TODO: Add transparent background dashboard images before uncommenting */ +/*.option_buttons {*/ +/* border-color: #444 !important;*/ +/*}*/ + +/*.button-label {*/ +/* color: var(--text) !important;*/ +/*}*/ + +/*DARK MODE TOGGLE */ +.toggle-link, +.toggle-link:active, +.toggle-link:focus { + color: #fff; +} + +/* Miscalleneous */ + +.tabs-animated .nav-link::before { + background-color: var(--primary); +} + +.tabs-animated-shadow .nav-link::before { + box-shadow: 0 16px 26px -10px var(--primary-border), 0 4px 25px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(31, 36, 64, 0.2); +} + +.font-icon-wrapper { + border-color: var(--table-border); +} + +.font-icon-wrapper:hover { + color: transparent; +} + + +.toggle-handle, +.toggle-handle:hover { + background-color: var(--switchery-background); +} + +/* jQuery TagsInput */ +[data-theme="dark"] div.tagsinput { + border: 1px solid var(--form-control-border) !important; + background: var(--form-control-bg) !important; + padding: 5px; + width: 300px; + height: 100px; + overflow-y: auto; +} + +[data-theme="dark"] div.tagsinput span.tag { + border: 1px solid #a5d24a; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + display: block; + float: left; + padding: 5px; + text-decoration: none; + background: #a0db2d !important; + color: #341 !important; + margin-right: 5px; + margin-bottom: 5px; + font-family: helvetica; + font-size: 13px; +} \ No newline at end of file diff --git a/week_5/activity-tracker/app/controllers/activities_controller.rb b/week_5/activity-tracker/app/controllers/activities_controller.rb index a1dc90c1..c0a107ea 100644 --- a/week_5/activity-tracker/app/controllers/activities_controller.rb +++ b/week_5/activity-tracker/app/controllers/activities_controller.rb @@ -3,13 +3,45 @@ class ActivitiesController < ApplicationController # GET /activities or /activities.json def index - @activities = Activity.all + @activities=Activity.where(user_id: current_user.id) end # GET /activities/1 or /activities/1.json def show end + def stats + @total_duration = Activity.where(user_id: current_user.id).sum(:duration) + @total_calories = Activity.where(user_id: current_user.id).sum(:calories) + end + + def pdf + require "prawn" + pdf1 = Prawn::Document.new + pdf1.text current_user.email,align: :center ,size: 30,style: :bold + pdf1.text "\nActivities\n\n",align: :center ,size: 25,style: :bold + @activities=Activity.where(user_id: current_user.id) + if @activities.any? + @activities.each do |activity| + pdf1.text "Activity Title : #{activity.title}",size: 20,style: :bold + act_img=StringIO.open(activity.image.download) + pdf1.image act_img,fit: [300,300] + pdf1.text "Activity Type : #{activity.activity_type}",size: 15 + pdf1.text "Activity Duration : #{activity.duration}",size: 15, inline_format: true + pdf1.text "Calories Burnt : #{activity.calories}",size: 15, inline_format: true + pdf1.start_new_page + end + else + pdf1.text "No Activity",size: 20,style: :bold + end + + pdf1.text "User Total Statistics\n\n",align: :center ,size: 25,style: :bold + stats + pdf1.text "Total Duration : #{@total_duration}",size: 20,align: :center, inline_format: true + pdf1.text "Calories Burnt : #{@total_calories}",size: 20,align: :center, inline_format: true + send_data(pdf1.render,filename: "#{current_user.email}.pdf",type: 'application/pdf' , disposition: 'inline') + end + # GET /activities/new def new @activity = Activity.new @@ -22,10 +54,10 @@ def edit # POST /activities or /activities.json def create @activity = Activity.new(activity_params) - + @activity.user_id=current_user.id respond_to do |format| if @activity.save - format.html { redirect_to activity_url(@activity), notice: "Activity was successfully created." } + format.html { redirect_to activities_url, notice: "Activity was successfully created." } format.json { render :show, status: :created, location: @activity } else format.html { render :new, status: :unprocessable_entity } @@ -52,7 +84,7 @@ def destroy @activity.destroy respond_to do |format| - format.html { redirect_to activities_url, notice: "Activity was successfully destroyed." } + format.html { redirect_to activities_url,status: :see_other, notice: "Activity was successfully destroyed." } format.json { head :no_content } end end @@ -65,6 +97,6 @@ def set_activity # Only allow a list of trusted parameters through. def activity_params - params.require(:activity).permit(:title, :activity_type, :start, :duration, :calories) + params.require(:activity).permit(:title, :activity_type, :start, :duration, :calories, :image) end end diff --git a/week_5/activity-tracker/app/controllers/application_controller.rb b/week_5/activity-tracker/app/controllers/application_controller.rb index 09705d12..6b4dcfa8 100644 --- a/week_5/activity-tracker/app/controllers/application_controller.rb +++ b/week_5/activity-tracker/app/controllers/application_controller.rb @@ -1,2 +1,3 @@ class ApplicationController < ActionController::Base + before_action :authenticate_user! end diff --git a/week_5/activity-tracker/app/controllers/static_pages_controller.rb b/week_5/activity-tracker/app/controllers/static_pages_controller.rb new file mode 100644 index 00000000..487a0e3f --- /dev/null +++ b/week_5/activity-tracker/app/controllers/static_pages_controller.rb @@ -0,0 +1,8 @@ +class StaticPagesController < ApplicationController + skip_before_action :authenticate_user!, only: %i[index] + def index + end + + def about + end +end diff --git a/week_5/activity-tracker/app/controllers/users/confirmations_controller.rb b/week_5/activity-tracker/app/controllers/users/confirmations_controller.rb new file mode 100644 index 00000000..fa535c0a --- /dev/null +++ b/week_5/activity-tracker/app/controllers/users/confirmations_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class Users::ConfirmationsController < Devise::ConfirmationsController + # GET /resource/confirmation/new + # def new + # super + # end + + # POST /resource/confirmation + # def create + # super + # end + + # GET /resource/confirmation?confirmation_token=abcdef + # def show + # super + # end + + # protected + + # The path used after resending confirmation instructions. + # def after_resending_confirmation_instructions_path_for(resource_name) + # super(resource_name) + # end + + # The path used after confirmation. + # def after_confirmation_path_for(resource_name, resource) + # super(resource_name, resource) + # end +end diff --git a/week_5/activity-tracker/app/controllers/users/omniauth_callbacks_controller.rb b/week_5/activity-tracker/app/controllers/users/omniauth_callbacks_controller.rb new file mode 100644 index 00000000..593f547d --- /dev/null +++ b/week_5/activity-tracker/app/controllers/users/omniauth_callbacks_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController + # You should configure your model like this: + # devise :omniauthable, omniauth_providers: [:twitter] + + # You should also create an action method in this controller like this: + # def twitter + # end + + # More info at: + # https://github.com/heartcombo/devise#omniauth + + # GET|POST /resource/auth/twitter + # def passthru + # super + # end + + # GET|POST /users/auth/twitter/callback + # def failure + # super + # end + + # protected + + # The path used when OmniAuth fails + # def after_omniauth_failure_path_for(scope) + # super(scope) + # end +end diff --git a/week_5/activity-tracker/app/controllers/users/passwords_controller.rb b/week_5/activity-tracker/app/controllers/users/passwords_controller.rb new file mode 100644 index 00000000..259dbb08 --- /dev/null +++ b/week_5/activity-tracker/app/controllers/users/passwords_controller.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class Users::PasswordsController < Devise::PasswordsController + # GET /resource/password/new + # def new + # super + # end + + # POST /resource/password + # def create + # super + # end + + # GET /resource/password/edit?reset_password_token=abcdef + # def edit + # super + # end + + # PUT /resource/password + # def update + # super + # end + + # protected + + # def after_resetting_password_path_for(resource) + # super(resource) + # end + + # The path used after sending reset password instructions + # def after_sending_reset_password_instructions_path_for(resource_name) + # super(resource_name) + # end +end diff --git a/week_5/activity-tracker/app/controllers/users/registrations_controller.rb b/week_5/activity-tracker/app/controllers/users/registrations_controller.rb new file mode 100644 index 00000000..b9e664fe --- /dev/null +++ b/week_5/activity-tracker/app/controllers/users/registrations_controller.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +class Users::RegistrationsController < Devise::RegistrationsController + # before_action :configure_sign_up_params, only: [:create] + # before_action :configure_account_update_params, only: [:update] + + # GET /resource/sign_up + # def new + # super + # end + + # POST /resource + # def create + # super + # end + + # GET /resource/edit + # def edit + # super + # end + + # PUT /resource + # def update + # super + # end + + # DELETE /resource + # def destroy + # super + # end + + # GET /resource/cancel + # Forces the session data which is usually expired after sign + # in to be expired now. This is useful if the user wants to + # cancel oauth signing in/up in the middle of the process, + # removing all OAuth session data. + # def cancel + # super + # end + + # protected + + # If you have extra params to permit, append them to the sanitizer. + # def configure_sign_up_params + # devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute]) + # end + + # If you have extra params to permit, append them to the sanitizer. + # def configure_account_update_params + # devise_parameter_sanitizer.permit(:account_update, keys: [:attribute]) + # end + + # The path used after sign up. + # def after_sign_up_path_for(resource) + # super(resource) + # end + + # The path used after sign up for inactive accounts. + # def after_inactive_sign_up_path_for(resource) + # super(resource) + # end +end diff --git a/week_5/activity-tracker/app/controllers/users/sessions_controller.rb b/week_5/activity-tracker/app/controllers/users/sessions_controller.rb new file mode 100644 index 00000000..a0f9b48e --- /dev/null +++ b/week_5/activity-tracker/app/controllers/users/sessions_controller.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class Users::SessionsController < Devise::SessionsController + # before_action :configure_sign_in_params, only: [:create] + + # GET /resource/sign_in + # def new + # super + # end + + # POST /resource/sign_in + # def create + # super + # end + + # DELETE /resource/sign_out + # def destroy + # super + # end + + # protected + + # If you have extra params to permit, append them to the sanitizer. + # def configure_sign_in_params + # devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute]) + # end +end diff --git a/week_5/activity-tracker/app/controllers/users/unlocks_controller.rb b/week_5/activity-tracker/app/controllers/users/unlocks_controller.rb new file mode 100644 index 00000000..2c410dc0 --- /dev/null +++ b/week_5/activity-tracker/app/controllers/users/unlocks_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class Users::UnlocksController < Devise::UnlocksController + # GET /resource/unlock/new + # def new + # super + # end + + # POST /resource/unlock + # def create + # super + # end + + # GET /resource/unlock?unlock_token=abcdef + # def show + # super + # end + + # protected + + # The path used after sending unlock password instructions + # def after_sending_unlock_instructions_path_for(resource) + # super(resource) + # end + + # The path used after unlocking the resource + # def after_unlock_path_for(resource) + # super(resource) + # end +end diff --git a/week_5/activity-tracker/app/helpers/activities_helper.rb b/week_5/activity-tracker/app/helpers/activities_helper.rb index 4e9784cc..08e9a699 100644 --- a/week_5/activity-tracker/app/helpers/activities_helper.rb +++ b/week_5/activity-tracker/app/helpers/activities_helper.rb @@ -1,2 +1,8 @@ module ActivitiesHelper + def flash_class(level) + case level + when :notice then 'alert alert-success' + when :alert then 'alert alert-danger' + end + end end diff --git a/week_5/activity-tracker/app/helpers/static_pages_helper.rb b/week_5/activity-tracker/app/helpers/static_pages_helper.rb new file mode 100644 index 00000000..2d63e79e --- /dev/null +++ b/week_5/activity-tracker/app/helpers/static_pages_helper.rb @@ -0,0 +1,2 @@ +module StaticPagesHelper +end diff --git a/week_5/activity-tracker/app/models/activity.rb b/week_5/activity-tracker/app/models/activity.rb index a99f990d..ff7c92e2 100644 --- a/week_5/activity-tracker/app/models/activity.rb +++ b/week_5/activity-tracker/app/models/activity.rb @@ -1,2 +1,5 @@ class Activity < ApplicationRecord + belongs_to :user + validates :calories, numericality: { only_integer: true } + has_one_attached :image end diff --git a/week_5/activity-tracker/app/models/type.rb b/week_5/activity-tracker/app/models/type.rb new file mode 100644 index 00000000..e17f3568 --- /dev/null +++ b/week_5/activity-tracker/app/models/type.rb @@ -0,0 +1,2 @@ +class Type < ApplicationRecord +end diff --git a/week_5/activity-tracker/app/models/user.rb b/week_5/activity-tracker/app/models/user.rb new file mode 100644 index 00000000..4cf0bb9a --- /dev/null +++ b/week_5/activity-tracker/app/models/user.rb @@ -0,0 +1,8 @@ +class User < ApplicationRecord + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :validatable + + has_many :activities +end diff --git a/week_5/activity-tracker/app/views/activities/_activity.html.erb b/week_5/activity-tracker/app/views/activities/_activity.html.erb index aab462e3..14fb4b53 100644 --- a/week_5/activity-tracker/app/views/activities/_activity.html.erb +++ b/week_5/activity-tracker/app/views/activities/_activity.html.erb @@ -1,27 +1,39 @@ -
-

- Title: - <%= activity.title %> -

- -

- Activity type: - <%= activity.activity_type %> -

- -

- Start: - <%= activity.start %> -

- -

- Duration: - <%= activity.duration %> -

- -

- Calories: - <%= activity.calories %> -

+
+

<%= activity.title %>

+
+
+
+ <%= image_tag activity.image ,class: 'img-fluid' %> +
+
+
+
+
<%= activity.activity_type %>
+
+
+
+
+ <%= link_to "".html_safe, edit_activity_path(activity), class: "btn btn-sm btn-secondary" %> +
+
+ <%= button_to "".html_safe, activity, method: :delete, form: {data: {turbo_confirm: "Are you sure?"}} , class: "btn btn-sm btn-secondary" %> +
+
+
+
+
+
+
<%= activity.start.in_time_zone.strftime("%d %b, %Y %I:%M %p") %>
+
+
+
+
+
<%= activity.calories %>
+
+
+
<%= activity.duration %>
+
+
+
diff --git a/week_5/activity-tracker/app/views/activities/_form.html.erb b/week_5/activity-tracker/app/views/activities/_form.html.erb index 6d4079c4..14ee233a 100644 --- a/week_5/activity-tracker/app/views/activities/_form.html.erb +++ b/week_5/activity-tracker/app/views/activities/_form.html.erb @@ -1,42 +1,65 @@ -<%= form_with(model: activity) do |form| %> - <% if activity.errors.any? %> -
-

<%= pluralize(activity.errors.count, "error") %> prohibited this activity from being saved:

- - -
- <% end %> -
- <%= form.label :title, style: "display: block" %> - <%= form.text_field :title %> -
+
+ <%= form.label :title, style: "display: block" %> + <%= form.text_field :title, class: "form-control mt-2" %> +
-
- <%= form.label :activity_type, style: "display: block" %> - <%= form.text_field :activity_type %> -
+
+
+ +
+ <%= form.collection_select :activity_type, Type.order(:id), :name, :name %> +
+ -
- <%= form.label :start, style: "display: block" %> - <%= form.datetime_field :start %> -
+
+ <%= form.label :start, style: "display: block" %> + <%= form.datetime_field :start, class:"form-control mt-2" %> +
-
- <%= form.label :duration, style: "display: block" %> - <%= form.text_field :duration %> -
+
+
+
+ <%= form.label :duration, style: "display: block" %> + <%= form.text_field :duration, class:"form-control mt-2" %> +
+
+
+
+ <%= form.label :calories, style: "display: block" %> + <%= form.number_field :calories, class:"form-control mt-2" %> +
+
+
-
- <%= form.label :calories, style: "display: block" %> - <%= form.number_field :calories %> -
+
+ <%= form.label :image, style: "display: block" %> + <%= form.file_field :image %> +
+ + +
+ <%= form.submit class: "btn mt-2 btn-primary text-dark"%> +
+ <% end %> -
- <%= form.submit %> +
-<% end %> + + + diff --git a/week_5/activity-tracker/app/views/activities/edit.html.erb b/week_5/activity-tracker/app/views/activities/edit.html.erb index 4774a05e..b6009c16 100644 --- a/week_5/activity-tracker/app/views/activities/edit.html.erb +++ b/week_5/activity-tracker/app/views/activities/edit.html.erb @@ -1,10 +1,5 @@ -

Editing activity

- -<%= render "form", activity: @activity %> - -
- -
- <%= link_to "Show this activity", @activity %> | - <%= link_to "Back to activities", activities_path %> -
+
+

Edit Activity

+ <%= render "form", activity: @activity %> +
+
\ No newline at end of file diff --git a/week_5/activity-tracker/app/views/activities/index.html.erb b/week_5/activity-tracker/app/views/activities/index.html.erb index 776f9042..4f2a68f6 100644 --- a/week_5/activity-tracker/app/views/activities/index.html.erb +++ b/week_5/activity-tracker/app/views/activities/index.html.erb @@ -1,14 +1,21 @@ -

<%= notice %>

+<% if notice %> + +<% end %> -

Activities

+

Activities<%= link_to "".html_safe, new_activity_path %>

-
- <% @activities.each do |activity| %> - <%= render activity %> -

- <%= link_to "Show this activity", activity %> -

+
+ <% if @activities.any? %> + <% @activities.each do |activity| %> +
+ <%= render activity %> +
+ <% end %> + <% else %> +

No Activities to show

<% end %>
- -<%= link_to "New activity", new_activity_path %> +<%= link_to "Back to Home", root_path %> diff --git a/week_5/activity-tracker/app/views/activities/new.html.erb b/week_5/activity-tracker/app/views/activities/new.html.erb index 855a5899..a3e82518 100644 --- a/week_5/activity-tracker/app/views/activities/new.html.erb +++ b/week_5/activity-tracker/app/views/activities/new.html.erb @@ -1,9 +1,6 @@ -

New activity

- -<%= render "form", activity: @activity %> - -
- -
- <%= link_to "Back to activities", activities_path %> +
+

New activity

+ <%= render "form", activity: @activity %> +
+ Back to Activities

diff --git a/week_5/activity-tracker/app/views/activities/show.html.erb b/week_5/activity-tracker/app/views/activities/show.html.erb index 73be1e19..42883dea 100644 --- a/week_5/activity-tracker/app/views/activities/show.html.erb +++ b/week_5/activity-tracker/app/views/activities/show.html.erb @@ -1,10 +1,5 @@

<%= notice %>

<%= render @activity %> - -
- <%= link_to "Edit this activity", edit_activity_path(@activity) %> | - <%= link_to "Back to activities", activities_path %> - - <%= button_to "Destroy this activity", @activity, method: :delete %> -
+

+Back to Activities

diff --git a/week_5/activity-tracker/app/views/activities/stats.html.erb b/week_5/activity-tracker/app/views/activities/stats.html.erb new file mode 100644 index 00000000..e0903570 --- /dev/null +++ b/week_5/activity-tracker/app/views/activities/stats.html.erb @@ -0,0 +1,15 @@ +
+
+
+
+

User Activity Statistics

+
+
+

+

Total duration - <%= @total_duration %>


+

Total calories burned - <%=@total_calories %>

+

+ Back to Activities +
+
+
\ No newline at end of file diff --git a/week_5/activity-tracker/app/views/layouts/application.html.erb b/week_5/activity-tracker/app/views/layouts/application.html.erb index 9c1804b3..8ab39dde 100644 --- a/week_5/activity-tracker/app/views/layouts/application.html.erb +++ b/week_5/activity-tracker/app/views/layouts/application.html.erb @@ -8,11 +8,15 @@ <%= csp_meta_tag %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> - <%= stylesheet_link_tag "dark-mode", "data-turbo-track": "reload" %> + <%= stylesheet_link_tag "light-mode", "data-turbo-track": "reload" %> + <%= javascript_importmap_tags %> - + - <%= yield %> + <%= render "shared/navbar" %> +
+ <%= yield %> +
diff --git a/week_5/activity-tracker/app/views/shared/_flash.html.erb b/week_5/activity-tracker/app/views/shared/_flash.html.erb new file mode 100644 index 00000000..9d6adaf4 --- /dev/null +++ b/week_5/activity-tracker/app/views/shared/_flash.html.erb @@ -0,0 +1,10 @@ +
+ <% flash.each do |key,value| %> + + <% end %> +
\ No newline at end of file diff --git a/week_5/activity-tracker/app/views/shared/_navbar.html.erb b/week_5/activity-tracker/app/views/shared/_navbar.html.erb new file mode 100644 index 00000000..cd024075 --- /dev/null +++ b/week_5/activity-tracker/app/views/shared/_navbar.html.erb @@ -0,0 +1,28 @@ + + diff --git a/week_5/activity-tracker/app/views/static_pages/about.html.erb b/week_5/activity-tracker/app/views/static_pages/about.html.erb new file mode 100644 index 00000000..5199267d --- /dev/null +++ b/week_5/activity-tracker/app/views/static_pages/about.html.erb @@ -0,0 +1,2 @@ +

About Us

+ diff --git a/week_5/activity-tracker/app/views/static_pages/index.html.erb b/week_5/activity-tracker/app/views/static_pages/index.html.erb new file mode 100644 index 00000000..155e9906 --- /dev/null +++ b/week_5/activity-tracker/app/views/static_pages/index.html.erb @@ -0,0 +1,14 @@ +

Welcome to Activity Tracker App



+ +<% if user_signed_in? %> +

Logged in as: <%= current_user.email %>



+ Activities

+ Edit Account settings

+ <%= button_to "Log out", destroy_user_session_path , method: :delete , form: {data: {turbo_confirm: "Do you want to Logout"}} %> +<% else %> +

New here ? , Then ->

+ Sign up

+

Already signed up , Then ->

+ Sign in

+ +<% end %> \ No newline at end of file diff --git a/week_5/activity-tracker/app/views/users/confirmations/new.html.erb b/week_5/activity-tracker/app/views/users/confirmations/new.html.erb new file mode 100644 index 00000000..4af186b2 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/confirmations/new.html.erb @@ -0,0 +1,16 @@ +

Resend confirmation instructions

+ +<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> + <%= render "users/shared/error_messages", resource: resource %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> +
+ +
+ <%= f.submit "Resend confirmation instructions" %> +
+<% end %> + +<%= render "users/shared/links" %> diff --git a/week_5/activity-tracker/app/views/users/mailer/confirmation_instructions.html.erb b/week_5/activity-tracker/app/views/users/mailer/confirmation_instructions.html.erb new file mode 100644 index 00000000..dc55f64f --- /dev/null +++ b/week_5/activity-tracker/app/views/users/mailer/confirmation_instructions.html.erb @@ -0,0 +1,5 @@ +

Welcome <%= @email %>!

+ +

You can confirm your account email through the link below:

+ +

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

diff --git a/week_5/activity-tracker/app/views/users/mailer/email_changed.html.erb b/week_5/activity-tracker/app/views/users/mailer/email_changed.html.erb new file mode 100644 index 00000000..32f4ba80 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/mailer/email_changed.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @email %>!

+ +<% if @resource.try(:unconfirmed_email?) %> +

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

+<% else %> +

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

+<% end %> diff --git a/week_5/activity-tracker/app/views/users/mailer/password_change.html.erb b/week_5/activity-tracker/app/views/users/mailer/password_change.html.erb new file mode 100644 index 00000000..b41daf47 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/mailer/password_change.html.erb @@ -0,0 +1,3 @@ +

Hello <%= @resource.email %>!

+ +

We're contacting you to notify you that your password has been changed.

diff --git a/week_5/activity-tracker/app/views/users/mailer/reset_password_instructions.html.erb b/week_5/activity-tracker/app/views/users/mailer/reset_password_instructions.html.erb new file mode 100644 index 00000000..f667dc12 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +

Hello <%= @resource.email %>!

+ +

Someone has requested a link to change your password. You can do this through the link below.

+ +

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

+ +

If you didn't request this, please ignore this email.

+

Your password won't change until you access the link above and create a new one.

diff --git a/week_5/activity-tracker/app/views/users/mailer/unlock_instructions.html.erb b/week_5/activity-tracker/app/views/users/mailer/unlock_instructions.html.erb new file mode 100644 index 00000000..41e148bf --- /dev/null +++ b/week_5/activity-tracker/app/views/users/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +

Hello <%= @resource.email %>!

+ +

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

+ +

Click the link below to unlock your account:

+ +

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

diff --git a/week_5/activity-tracker/app/views/users/passwords/edit.html.erb b/week_5/activity-tracker/app/views/users/passwords/edit.html.erb new file mode 100644 index 00000000..863ffbb2 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/passwords/edit.html.erb @@ -0,0 +1,25 @@ +

Change your password

+ +<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> + <%= render "users/shared/error_messages", resource: resource %> + <%= f.hidden_field :reset_password_token %> + +
+ <%= f.label :password, "New password" %>
+ <% if @minimum_password_length %> + (<%= @minimum_password_length %> characters minimum)
+ <% end %> + <%= f.password_field :password, autofocus: true, autocomplete: "new-password" %> +
+ +
+ <%= f.label :password_confirmation, "Confirm new password" %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %> +
+ +
+ <%= f.submit "Change my password" %> +
+<% end %> + +<%= render "users/shared/links" %> diff --git a/week_5/activity-tracker/app/views/users/passwords/new.html.erb b/week_5/activity-tracker/app/views/users/passwords/new.html.erb new file mode 100644 index 00000000..3b30b06d --- /dev/null +++ b/week_5/activity-tracker/app/views/users/passwords/new.html.erb @@ -0,0 +1,16 @@ +

Forgot your password?

+ +<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> + <%= render "users/shared/error_messages", resource: resource %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ +
+ <%= f.submit "Send me reset password instructions" %> +
+<% end %> + +<%= render "users/shared/links" %> diff --git a/week_5/activity-tracker/app/views/users/registrations/edit.html.erb b/week_5/activity-tracker/app/views/users/registrations/edit.html.erb new file mode 100644 index 00000000..038cd945 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/registrations/edit.html.erb @@ -0,0 +1,43 @@ +

Edit <%= resource_name.to_s.humanize %>

+ +<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> + <%= render "users/shared/error_messages", resource: resource %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ + <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> +
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
+ <% end %> + +
+ <%= f.label :password %> (leave blank if you don't want to change it)
+ <%= f.password_field :password, autocomplete: "new-password" %> + <% if @minimum_password_length %> +
+ <%= @minimum_password_length %> characters minimum + <% end %> +
+ +
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %> +
+ +
+ <%= f.label :current_password %> (we need your current password to confirm your changes)
+ <%= f.password_field :current_password, autocomplete: "current-password" %> +
+ +
+ <%= f.submit "Update" %> +
+<% end %> + +

Cancel my account

+ +

Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>

+ +<%= link_to "Back", :back %> diff --git a/week_5/activity-tracker/app/views/users/registrations/new.html.erb b/week_5/activity-tracker/app/views/users/registrations/new.html.erb new file mode 100644 index 00000000..61d1e4c7 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/registrations/new.html.erb @@ -0,0 +1,29 @@ +

Sign up

+ +<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> + <%= render "users/shared/error_messages", resource: resource %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ +
+ <%= f.label :password %> + <% if @minimum_password_length %> + (<%= @minimum_password_length %> characters minimum) + <% end %>
+ <%= f.password_field :password, autocomplete: "new-password" %> +
+ +
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %> +
+ +
+ <%= f.submit "Sign up" %> +
+<% end %> + +<%= render "users/shared/links" %> diff --git a/week_5/activity-tracker/app/views/users/sessions/new.html.erb b/week_5/activity-tracker/app/views/users/sessions/new.html.erb new file mode 100644 index 00000000..4d58a142 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/sessions/new.html.erb @@ -0,0 +1,26 @@ +

Log in

+ +<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> +
+ <% f.label :email %>
+ <%= f.email_field :email, class:"form-control",placeholder:"Email" ,autofocus: true, autocomplete: "email" %> +
+ +
+ <% f.label :password %>
+ <%= f.password_field :password,class:"form-control",placeholder:"Password", autocomplete: "current-password" %> +
+
+ <% if devise_mapping.rememberable? %> +
+ <%= f.check_box :remember_me , class:"form-check-input" %> + <%= f.label :remember_me %> +
+ <% end %> +
+
+ <%= f.submit "Log in" ,class:"btn btn-primary" %> +
+<% end %> +
+<%= render "users/shared/links" %> diff --git a/week_5/activity-tracker/app/views/users/shared/_error_messages.html.erb b/week_5/activity-tracker/app/views/users/shared/_error_messages.html.erb new file mode 100644 index 00000000..ba7ab887 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/shared/_error_messages.html.erb @@ -0,0 +1,15 @@ +<% if resource.errors.any? %> +
+

+ <%= I18n.t("errors.messages.not_saved", + count: resource.errors.count, + resource: resource.class.model_name.human.downcase) + %> +

+
    + <% resource.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+<% end %> diff --git a/week_5/activity-tracker/app/views/users/shared/_links.html.erb b/week_5/activity-tracker/app/views/users/shared/_links.html.erb new file mode 100644 index 00000000..facaffc8 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/shared/_links.html.erb @@ -0,0 +1,25 @@ +<%- if controller_name != 'sessions' %> + <%= link_to "Log in", new_session_path(resource_name) %>
+<% end %> + +<%- if devise_mapping.registerable? && controller_name != 'registrations' %> + <%= link_to "Sign up", new_registration_path(resource_name),class:"btn btn-info" %>
+<% end %> +
+<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> + <%= link_to "Forgot your password?", new_password_path(resource_name),class:"btn btn-info" %>
+<% end %> + +<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> + <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+<% end %> + +<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> + <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
+<% end %> + +<%- if devise_mapping.omniauthable? %> + <%- resource_class.omniauth_providers.each do |provider| %> + <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), method: :post %>
+ <% end %> +<% end %> diff --git a/week_5/activity-tracker/app/views/users/unlocks/new.html.erb b/week_5/activity-tracker/app/views/users/unlocks/new.html.erb new file mode 100644 index 00000000..2f4fab84 --- /dev/null +++ b/week_5/activity-tracker/app/views/users/unlocks/new.html.erb @@ -0,0 +1,16 @@ +

Resend unlock instructions

+ +<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> + <%= render "users/shared/error_messages", resource: resource %> + +
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %> +
+ +
+ <%= f.submit "Resend unlock instructions" %> +
+<% end %> + +<%= render "users/shared/links" %> diff --git a/week_5/activity-tracker/config/initializers/devise.rb b/week_5/activity-tracker/config/initializers/devise.rb index ddce73fb..91de1780 100644 --- a/week_5/activity-tracker/config/initializers/devise.rb +++ b/week_5/activity-tracker/config/initializers/devise.rb @@ -25,7 +25,7 @@ # note that it will be overwritten if you use your own mailer class # with default "from" parameter. config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com' - + config.navigational_formats = ['*/*', :html , :turbo_stream] # Configure the class responsible to send e-mails. # config.mailer = 'Devise::Mailer' diff --git a/week_5/activity-tracker/config/routes.rb b/week_5/activity-tracker/config/routes.rb index ba195b7b..ead2fd3e 100644 --- a/week_5/activity-tracker/config/routes.rb +++ b/week_5/activity-tracker/config/routes.rb @@ -1,8 +1,15 @@ Rails.application.routes.draw do - resources :activities - - # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html - - # Defines the root path route ("/") - root "activities#index" + devise_for :users, controllers: { + sessions: 'users/sessions', + registrations: 'users/registrations', + passwords: 'users/passwords' + } + get "activities/pdf" , to: "activities#pdf" + root "static_pages#index" + get "about", to: "static_pages#about" + resources :activities do + collection do + get 'stats' + end + end end diff --git a/week_5/activity-tracker/db/migrate/20230214171052_devise_create_users.rb b/week_5/activity-tracker/db/migrate/20230214171052_devise_create_users.rb new file mode 100644 index 00000000..43927dbd --- /dev/null +++ b/week_5/activity-tracker/db/migrate/20230214171052_devise_create_users.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class DeviseCreateUsers < ActiveRecord::Migration[7.0] + def change + create_table :users do |t| + ## Database authenticatable + t.string :email, null: false, default: "" + t.string :encrypted_password, null: false, default: "" + + ## Recoverable + t.string :reset_password_token + t.datetime :reset_password_sent_at + + ## Rememberable + t.datetime :remember_created_at + + ## Trackable + # t.integer :sign_in_count, default: 0, null: false + # t.datetime :current_sign_in_at + # t.datetime :last_sign_in_at + # t.string :current_sign_in_ip + # t.string :last_sign_in_ip + + ## Confirmable + # t.string :confirmation_token + # t.datetime :confirmed_at + # t.datetime :confirmation_sent_at + # t.string :unconfirmed_email # Only if using reconfirmable + + ## Lockable + # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts + # t.string :unlock_token # Only if unlock strategy is :email or :both + # t.datetime :locked_at + + + t.timestamps null: false + end + + add_index :users, :email, unique: true + add_index :users, :reset_password_token, unique: true + # add_index :users, :confirmation_token, unique: true + # add_index :users, :unlock_token, unique: true + end +end diff --git a/week_5/activity-tracker/db/migrate/20230223183705_add_user_id_to_activities.rb b/week_5/activity-tracker/db/migrate/20230223183705_add_user_id_to_activities.rb new file mode 100644 index 00000000..77401dea --- /dev/null +++ b/week_5/activity-tracker/db/migrate/20230223183705_add_user_id_to_activities.rb @@ -0,0 +1,6 @@ +class AddUserIdToActivities < ActiveRecord::Migration[7.0] + def change + add_column :activities, :user_id, :integer + add_index :activities, :user_id + end +end diff --git a/week_5/activity-tracker/db/migrate/20230223230401_create_types.rb b/week_5/activity-tracker/db/migrate/20230223230401_create_types.rb new file mode 100644 index 00000000..554dba11 --- /dev/null +++ b/week_5/activity-tracker/db/migrate/20230223230401_create_types.rb @@ -0,0 +1,9 @@ +class CreateTypes < ActiveRecord::Migration[7.0] + def change + create_table :types do |t| + t.string :name + + t.timestamps + end + end +end diff --git a/week_5/activity-tracker/db/migrate/20230224045742_create_active_storage_tables.active_storage.rb b/week_5/activity-tracker/db/migrate/20230224045742_create_active_storage_tables.active_storage.rb new file mode 100644 index 00000000..8a7bfe18 --- /dev/null +++ b/week_5/activity-tracker/db/migrate/20230224045742_create_active_storage_tables.active_storage.rb @@ -0,0 +1,57 @@ +# This migration comes from active_storage (originally 20170806125915) +class CreateActiveStorageTables < ActiveRecord::Migration[5.2] + def change + # Use Active Record's configured type for primary and foreign keys + primary_key_type, foreign_key_type = primary_and_foreign_key_types + + create_table :active_storage_blobs, id: primary_key_type do |t| + t.string :key, null: false + t.string :filename, null: false + t.string :content_type + t.text :metadata + t.string :service_name, null: false + t.bigint :byte_size, null: false + t.string :checksum + + if connection.supports_datetime_with_precision? + t.datetime :created_at, precision: 6, null: false + else + t.datetime :created_at, null: false + end + + t.index [ :key ], unique: true + end + + create_table :active_storage_attachments, id: primary_key_type do |t| + t.string :name, null: false + t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type + t.references :blob, null: false, type: foreign_key_type + + if connection.supports_datetime_with_precision? + t.datetime :created_at, precision: 6, null: false + else + t.datetime :created_at, null: false + end + + t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + + create_table :active_storage_variant_records, id: primary_key_type do |t| + t.belongs_to :blob, null: false, index: false, type: foreign_key_type + t.string :variation_digest, null: false + + t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + end + + private + def primary_and_foreign_key_types + config = Rails.configuration.generators + setting = config.options[config.orm][:primary_key_type] + primary_key_type = setting || :primary_key + foreign_key_type = setting || :bigint + [primary_key_type, foreign_key_type] + end +end diff --git a/week_5/activity-tracker/db/schema.rb b/week_5/activity-tracker/db/schema.rb index 2e77949c..459cdbc8 100644 --- a/week_5/activity-tracker/db/schema.rb +++ b/week_5/activity-tracker/db/schema.rb @@ -10,7 +10,35 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_02_05_151240) do +ActiveRecord::Schema[7.0].define(version: 2023_02_24_045742) do + create_table "active_storage_attachments", force: :cascade do |t| + t.string "name", null: false + t.string "record_type", null: false + t.bigint "record_id", null: false + t.bigint "blob_id", null: false + t.datetime "created_at", null: false + t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" + t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true + end + + create_table "active_storage_blobs", force: :cascade do |t| + t.string "key", null: false + t.string "filename", null: false + t.string "content_type" + t.text "metadata" + t.string "service_name", null: false + t.bigint "byte_size", null: false + t.string "checksum" + t.datetime "created_at", null: false + t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true + end + + create_table "active_storage_variant_records", force: :cascade do |t| + t.bigint "blob_id", null: false + t.string "variation_digest", null: false + t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true + end + create_table "activities", force: :cascade do |t| t.string "title" t.string "activity_type" @@ -19,6 +47,28 @@ t.integer "calories" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "user_id" + t.index ["user_id"], name: "index_activities_on_user_id" + end + + create_table "types", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "users", force: :cascade do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["email"], name: "index_users_on_email", unique: true + t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end + add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" + add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" end diff --git a/week_5/activity-tracker/storage/lh/gg/lhggljjue94x9eo87d7tvza580d2 b/week_5/activity-tracker/storage/lh/gg/lhggljjue94x9eo87d7tvza580d2 new file mode 100644 index 00000000..57e2fa60 Binary files /dev/null and b/week_5/activity-tracker/storage/lh/gg/lhggljjue94x9eo87d7tvza580d2 differ diff --git a/week_5/activity-tracker/storage/xj/r0/xjr0mp1qjyyboxv9171k1spbr5qh b/week_5/activity-tracker/storage/xj/r0/xjr0mp1qjyyboxv9171k1spbr5qh new file mode 100644 index 00000000..c6cbee24 Binary files /dev/null and b/week_5/activity-tracker/storage/xj/r0/xjr0mp1qjyyboxv9171k1spbr5qh differ diff --git a/week_5/activity-tracker/test/controllers/static_pages_controller_test.rb b/week_5/activity-tracker/test/controllers/static_pages_controller_test.rb new file mode 100644 index 00000000..2126daec --- /dev/null +++ b/week_5/activity-tracker/test/controllers/static_pages_controller_test.rb @@ -0,0 +1,13 @@ +require "test_helper" + +class StaticPagesControllerTest < ActionDispatch::IntegrationTest + test "should get index" do + get static_pages_index_url + assert_response :success + end + + test "should get about" do + get static_pages_about_url + assert_response :success + end +end diff --git a/week_5/activity-tracker/test/fixtures/types.yml b/week_5/activity-tracker/test/fixtures/types.yml new file mode 100644 index 00000000..7d412240 --- /dev/null +++ b/week_5/activity-tracker/test/fixtures/types.yml @@ -0,0 +1,7 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + +two: + name: MyString diff --git a/week_5/activity-tracker/test/fixtures/users.yml b/week_5/activity-tracker/test/fixtures/users.yml new file mode 100644 index 00000000..d7a33292 --- /dev/null +++ b/week_5/activity-tracker/test/fixtures/users.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the "{}" from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/week_5/activity-tracker/test/models/type_test.rb b/week_5/activity-tracker/test/models/type_test.rb new file mode 100644 index 00000000..3156d0a7 --- /dev/null +++ b/week_5/activity-tracker/test/models/type_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class TypeTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/week_5/activity-tracker/test/models/user_test.rb b/week_5/activity-tracker/test/models/user_test.rb new file mode 100644 index 00000000..5c07f490 --- /dev/null +++ b/week_5/activity-tracker/test/models/user_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class UserTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end