├── main ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0002_alter_comment_author.py │ ├── 0003_alter_project_user.py │ ├── 0004_message.py │ └── 0001_initial.py ├── apps.py ├── admin.py ├── urls.py ├── models.py └── views.py ├── users ├── __init__.py ├── migrations │ ├── __init__.py │ ├── 0007_alter_account_other_skills.py │ ├── 0005_alter_account_other_skills.py │ ├── 0008_alter_account_other_skills.py │ ├── 0002_auto_20210619_1313.py │ ├── 0006_auto_20210619_2142.py │ ├── 0003_auto_20210619_1325.py │ ├── 0004_auto_20210619_1511.py │ └── 0001_initial.py ├── admin.py ├── apps.py ├── signals.py ├── urls.py ├── models.py └── views.py ├── devSearch ├── __init__.py ├── asgi.py ├── wsgi.py ├── urls.py └── settings.py ├── .gitignore ├── media └── avatars │ └── default.jpg ├── static ├── images │ ├── favicon.ico │ ├── pattern.jpg │ ├── project-a.png │ ├── project-b.png │ ├── project-c.png │ ├── Devsearch Home.jpg │ ├── Devsearch Inbox.jpg │ ├── DevSearch Projects.jpg │ ├── Devsearch Profile.jpg │ ├── icon.svg │ └── logo.svg ├── uikit │ ├── app.js │ ├── styles │ │ ├── modules │ │ │ ├── _avatar.css │ │ │ ├── _layout.css │ │ │ ├── _animation.css │ │ │ ├── _base.css │ │ │ ├── _variables.css │ │ │ ├── _tag-input.css │ │ │ ├── _card.css │ │ │ ├── _author-box.css │ │ │ ├── _typography.css │ │ │ ├── _alert.css │ │ │ ├── _grid.css │ │ │ ├── _utilities.css │ │ │ ├── _tag.css │ │ │ ├── _loading.css │ │ │ ├── _button.css │ │ │ └── _form.css │ │ └── uikit.css │ ├── app.css │ └── index.html └── styles │ └── app.css ├── .deepsource.toml ├── requirements.txt ├── manage.py ├── templates ├── reset_password_complete.html ├── reset_password_sent.html ├── forgetpassword.html ├── reset.html ├── reset_password.html ├── login.html ├── message.html ├── inbox.html ├── delete.html ├── signup.html ├── send-message.html ├── skill-add-edit-form.html ├── project-add-edit-form.html ├── single-project.html ├── index.html ├── account-edit-form.html ├── projects.html ├── profile.html └── account.html └── README.md /main/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /users/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /devSearch/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /users/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | .idea 3 | media 4 | .env 5 | *.pyc -------------------------------------------------------------------------------- /media/avatars/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h4cktivist/devSearch/HEAD/media/avatars/default.jpg -------------------------------------------------------------------------------- /static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h4cktivist/devSearch/HEAD/static/images/favicon.ico -------------------------------------------------------------------------------- /static/images/pattern.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h4cktivist/devSearch/HEAD/static/images/pattern.jpg -------------------------------------------------------------------------------- /static/images/project-a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h4cktivist/devSearch/HEAD/static/images/project-a.png -------------------------------------------------------------------------------- /static/images/project-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h4cktivist/devSearch/HEAD/static/images/project-b.png -------------------------------------------------------------------------------- /static/images/project-c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h4cktivist/devSearch/HEAD/static/images/project-c.png -------------------------------------------------------------------------------- /static/images/Devsearch Home.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h4cktivist/devSearch/HEAD/static/images/Devsearch Home.jpg -------------------------------------------------------------------------------- /static/images/Devsearch Inbox.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h4cktivist/devSearch/HEAD/static/images/Devsearch Inbox.jpg -------------------------------------------------------------------------------- /static/images/DevSearch Projects.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h4cktivist/devSearch/HEAD/static/images/DevSearch Projects.jpg -------------------------------------------------------------------------------- /static/images/Devsearch Profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h4cktivist/devSearch/HEAD/static/images/Devsearch Profile.jpg -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "python" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | runtime_version = "3.x.x" -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.3.4 2 | Django==3.2.20 3 | Pillow==10.2.0 4 | psycopg2==2.9.1 5 | python-dotenv==0.17.1 6 | pytz==2021.1 7 | sqlparse==0.4.2 8 | -------------------------------------------------------------------------------- /static/uikit/app.js: -------------------------------------------------------------------------------- 1 | // Invoke Functions Call on Document Loaded 2 | document.addEventListener('DOMContentLoaded', function () { 3 | hljs.highlightAll(); 4 | }); 5 | -------------------------------------------------------------------------------- /main/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MainConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'main' 7 | -------------------------------------------------------------------------------- /users/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Account, Link, Skill 3 | 4 | 5 | admin.site.register(Account) 6 | admin.site.register(Link) 7 | admin.site.register(Skill) 8 | -------------------------------------------------------------------------------- /main/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Project, Comment, Message 4 | 5 | admin.site.register(Project) 6 | admin.site.register(Comment) 7 | admin.site.register(Message) 8 | -------------------------------------------------------------------------------- /users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsersConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'users' 7 | 8 | def ready(self): 9 | import users.signals 10 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_avatar.css: -------------------------------------------------------------------------------- 1 | .avatar { 2 | border-radius: 50%; 3 | border: 2px solid var(--color-main); 4 | object-fit: cover; 5 | } 6 | 7 | .avatar--xl { 8 | height: 20rem; 9 | width: 20rem; 10 | } 11 | 12 | .avatar--lg { 13 | height: 15rem; 14 | width: 15rem; 15 | } 16 | 17 | .avatar--md { 18 | height: 7rem; 19 | width: 7rem; 20 | } 21 | 22 | .avatar--sm { 23 | height: 5rem; 24 | width: 5rem; 25 | } 26 | -------------------------------------------------------------------------------- /devSearch/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for devSearch project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'devSearch.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /devSearch/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for devSearch project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'devSearch.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /main/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | 5 | urlpatterns = [ 6 | path('', views.index, name='index'), 7 | path('projects/', views.projects, name='projects'), 8 | path('projects/', views.singleProject, name='single-project'), 9 | 10 | path('messages/', views.inbox, name='inbox'), 11 | path('messages/', views.singleMessage, name='message'), 12 | path('messages/send/', views.sendMessage, name='send-message'), 13 | ] 14 | -------------------------------------------------------------------------------- /users/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save 2 | from django.dispatch import receiver 3 | 4 | from django.contrib.auth.models import User 5 | from .models import Account 6 | 7 | 8 | @receiver(post_save, sender=User) 9 | def create_account(sender, instance, created, **kwargs): 10 | if created: 11 | Account.objects.create(user=instance) 12 | 13 | 14 | @receiver(post_save, sender=User) 15 | def save_account(sender, instance, **kwargs): 16 | instance.account.save() 17 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_layout.css: -------------------------------------------------------------------------------- 1 | .layout { 2 | display: flex; 3 | flex-flow: row wrap; 4 | justify-content: space-between; 5 | } 6 | 7 | .layout > .column { 8 | margin-bottom: 3rem; 9 | } 10 | 11 | .layout > .column.column--1of3 { 12 | flex-basis: 30%; 13 | } 14 | 15 | .layout > .column.column--2of3 { 16 | flex-basis: 64%; 17 | } 18 | 19 | .layout > .column.column--1of2 { 20 | flex-basis: 48%; 21 | } 22 | 23 | @media screen and (max-width: 1080px) { 24 | .layout > .column { 25 | flex-basis: 100% !important; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_animation.css: -------------------------------------------------------------------------------- 1 | @keyframes move-in-left { 2 | 0% { 3 | width: 0%; 4 | transform: scale(2); 5 | } 6 | 7 | 80% { 8 | width: 70%; 9 | transform: scale(1); 10 | } 11 | 12 | 100% { 13 | width: 100%; 14 | } 15 | } 16 | 17 | @keyframes loading { 18 | from { 19 | transform: scale(0, 0); 20 | } 21 | 22 | to { 23 | transform: scale(1, 1); 24 | } 25 | } 26 | 27 | @keyframes spin { 28 | 0% { 29 | transform: rotate(0deg); 30 | } 31 | 32 | 100% { 33 | transform: rotate(360deg); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /users/migrations/0007_alter_account_other_skills.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-06-19 16:45 2 | 3 | import django.contrib.postgres.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0006_auto_20210619_2142'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='account', 16 | name='other_skills', 17 | field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=30, verbose_name='Other skills'), size=None), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /users/migrations/0005_alter_account_other_skills.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-06-19 13:14 2 | 3 | import django.contrib.postgres.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0004_auto_20210619_1511'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='account', 16 | name='other_skills', 17 | field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=30, verbose_name='Other skills'), blank=True, size=None), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /users/migrations/0008_alter_account_other_skills.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-06-19 16:46 2 | 3 | import django.contrib.postgres.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0007_alter_account_other_skills'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='account', 16 | name='other_skills', 17 | field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=30, verbose_name='Other skills'), null=True, size=None), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /main/migrations/0002_alter_comment_author.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-07-01 05:22 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('main', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='comment', 18 | name='author', 19 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /main/migrations/0003_alter_project_user.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-07-01 05:38 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('main', '0002_alter_comment_author'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='project', 18 | name='user', 19 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'devSearch.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_base.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | text-rendering: optimizeLegibility; 6 | color: inherit; 7 | font-size: inherit; 8 | } 9 | 10 | html { 11 | font-size: 50%; 12 | } 13 | 14 | @media only screen and (min-width: 480px) { 15 | html { 16 | font-size: 56.25%; 17 | } 18 | } 19 | 20 | @media only screen and (min-width: 1200px) { 21 | html { 22 | font-size: 62.5%; 23 | } 24 | } 25 | 26 | @media only screen and (min-width: 2100px) { 27 | html { 28 | font-size: 75%; 29 | } 30 | } 31 | 32 | body { 33 | line-height: 1.6; 34 | font-weight: 400; 35 | font-size: 1.5rem; 36 | color: var(--color-text); 37 | background-color: var(--color-bg); 38 | min-height: 100vh; 39 | } 40 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-main: #5aa5b9; 3 | --color-main-light: #e6faff; 4 | --color-sub: #3f4156; 5 | --color-sub-light: #51546e; 6 | --color-sub-lighter: #ededfd; 7 | --color-text: #737373; 8 | --color-gray: #8b8b8b; 9 | --color-light: #e5e7eb; 10 | --color-light-gray: #767676; 11 | --color-bg: #f8fafd; 12 | --color-white: #fffefd; 13 | --color-white-light: #f3f3f3; 14 | --color-success: #359e64; 15 | --color-success-bg: #def8e8; 16 | --color-error: #fc4b0b; 17 | --color-error-bg: #fff2ee; 18 | --font-base: 'Poppins', arial, helvetica, 'Segoe UI', roboto, ubuntu, sans-serif; 19 | --font-monospace: 'Fira Code', 'Courier New', courier, monospace; 20 | --font-regular: 300; 21 | --font-medium: 500; 22 | --font-bold: 700; 23 | --generic-shadow: 1px 1px 5px #00000010; 24 | --generic-transition: all 0.3s ease-in-out; 25 | } 26 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_tag-input.css: -------------------------------------------------------------------------------- 1 | .input-tags { 2 | width: 100%; 3 | border-radius: 0.5rem; 4 | border: 2px solid var(--color-light); 5 | padding: 0.6rem 0.8rem; 6 | display: flex; 7 | flex-wrap: wrap; 8 | transition: all 0.3s ease-in-out; 9 | cursor: text; 10 | } 11 | 12 | .input-tags:focus-within { 13 | border: 2px solid var(--color-main); 14 | } 15 | 16 | .input-tags input { 17 | border: none; 18 | font-size: 14px; 19 | padding: 0.5rem 1rem; 20 | width: 100%; 21 | } 22 | 23 | .input-tags input:focus { 24 | outline: none; 25 | } 26 | 27 | .input-tag-list { 28 | display: flex; 29 | column-gap: 4px; 30 | flex-wrap: wrap !important; 31 | row-gap: 4px; 32 | } 33 | 34 | .input-tag-item { 35 | white-space: nowrap; 36 | } 37 | 38 | .input-tag-item small { 39 | margin-right: 6px; 40 | } 41 | 42 | .input-tag-item i { 43 | color: var(--color-white); 44 | cursor: pointer; 45 | } 46 | -------------------------------------------------------------------------------- /static/uikit/styles/uikit.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Mumble UI 3 | * Version: v0.1.1 4 | */ 5 | 6 | /* Fonts */ 7 | @import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400&family=Poppins:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap'); 8 | 9 | /* Mumble UI Styles */ 10 | @import url('./modules/_variables.css'); 11 | @import url('./modules/_base.css'); 12 | @import url('./modules/_typography.css'); 13 | @import url('./modules/_utilities.css'); 14 | @import url('./modules/_animation.css'); 15 | @import url('./modules/_avatar.css'); 16 | @import url('./modules/_button.css'); 17 | @import url('./modules/_card.css'); 18 | @import url('./modules/_form.css'); 19 | @import url('./modules/_tag.css'); 20 | @import url('./modules/_author-box.css'); 21 | @import url('./modules/_alert.css'); 22 | @import url('./modules/_loading.css'); 23 | @import url('./modules/_grid.css'); 24 | @import url('./modules/_layout.css'); 25 | -------------------------------------------------------------------------------- /users/migrations/0002_auto_20210619_1313.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-06-19 08:13 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='account', 15 | name='links', 16 | ), 17 | migrations.AddField( 18 | model_name='account', 19 | name='links', 20 | field=models.ManyToManyField(related_name='Links', to='users.Link'), 21 | ), 22 | migrations.RemoveField( 23 | model_name='account', 24 | name='skills', 25 | ), 26 | migrations.AddField( 27 | model_name='account', 28 | name='skills', 29 | field=models.ManyToManyField(related_name='Skills', to='users.Skill'), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /users/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from users import views 3 | 4 | 5 | urlpatterns = [ 6 | path('signup/', views.signup, name='singup'), 7 | path('login', views.signin, name='login'), 8 | path('logout/', views.logOut, name='logout'), 9 | path('account/', views.account, name='account'), 10 | path('profile/', views.profile, name='profile'), 11 | 12 | path('account/edit-account', views.editAccount, name='edit-account'), 13 | 14 | path('account/add-skill', views.addSkill, name='add-skill'), 15 | path('account/edit-skill/', views.editSkill, name='edit-skill'), 16 | path('account/delete-skill/', views.deleteSkill, name='delete-skill'), 17 | 18 | path('account/add-project', views.addProject, name='add-project'), 19 | path('account/edit-project/', views.editProject, name='edit-project'), 20 | path('account/delete-project/', views.deleteProject, name='delete-project'), 21 | 22 | ] 23 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_card.css: -------------------------------------------------------------------------------- 1 | .card { 2 | border-radius: 1rem; 3 | background-color: var(--color-white); 4 | border: 1.5px solid var(--color-light); 5 | width: auto; 6 | height: auto; 7 | margin-top: 0.5rem; 8 | margin-bottom: 0.5rem; 9 | overflow: hidden; 10 | } 11 | 12 | .card__link { 13 | transition: all 0.3s ease-in; 14 | } 15 | 16 | .card__link:hover { 17 | text-decoration: none; 18 | } 19 | 20 | .card__body, 21 | .card__header { 22 | padding: 2rem 2.5rem; 23 | } 24 | 25 | .card__header { 26 | padding-top: 1.5rem; 27 | padding-bottom: 1.5rem; 28 | border-bottom: 1.5px solid var(--color-light); 29 | } 30 | 31 | .card__headerTitle { 32 | font-size: 2.2rem; 33 | font-weight: var(--font-medium); 34 | } 35 | 36 | .card__header .card__link:hover { 37 | text-decoration: underline; 38 | color: var(--color-main); 39 | } 40 | 41 | .card.card--dark { 42 | background-color: var(--color-light); 43 | } 44 | 45 | .card.card--dark .card__header { 46 | box-shadow: var(--generic-shadow); 47 | } 48 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_author-box.css: -------------------------------------------------------------------------------- 1 | .author-box { 2 | display: inline-flex; 3 | align-items: center; 4 | justify-content: space-between; 5 | } 6 | 7 | .author-box__info { 8 | margin-left: 1rem; 9 | line-height: 1.2; 10 | } 11 | 12 | .author-box--md .author-box__info { 13 | margin-left: 1.6rem; 14 | line-height: 1.3; 15 | } 16 | 17 | .author-box--lg .author-box__info { 18 | margin-left: 2.8rem; 19 | line-height: 1.6; 20 | } 21 | 22 | .author-box__name { 23 | font-size: 1.5rem; 24 | font-weight: var(--font-medium); 25 | color: var(--color-sub); 26 | } 27 | 28 | .author-box--md .author-box__name { 29 | font-size: 1.65rem; 30 | } 31 | 32 | .author-box--lg .author-box__name { 33 | font-size: 2.4rem; 34 | } 35 | 36 | .author-box__handle { 37 | font-size: 1.4rem; 38 | color: var(--color-text); 39 | font-weight: var(--font-regular); 40 | } 41 | 42 | .author-box--md .author-box__handle { 43 | font-size: 1.5rem; 44 | } 45 | 46 | .author-box--lg .author-box__handle { 47 | font-size: 1.65rem; 48 | } 49 | -------------------------------------------------------------------------------- /static/images/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_typography.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: var(--font-base); 3 | } 4 | 5 | h1 { 6 | font-size: 4.8rem; 7 | font-weight: var(--font-medium); 8 | } 9 | 10 | h2 { 11 | font-size: 3.6rem; 12 | font-weight: var(--font-bold); 13 | } 14 | 15 | h3 { 16 | font-size: 3.2rem; 17 | font-weight: var(--font-medium); 18 | } 19 | 20 | h4 { 21 | font-size: 2.8rem; 22 | font-weight: var(--font-medium); 23 | } 24 | 25 | h5 { 26 | font-size: 2.4rem; 27 | font-weight: var(--font-medium); 28 | } 29 | 30 | h5, 31 | h6 { 32 | font-weight: var(--font-regular); 33 | } 34 | 35 | h6 { 36 | font-size: 1.8rem; 37 | } 38 | 39 | p, 40 | span, 41 | strong { 42 | font-size: 1.6rem; 43 | color: var(--color-text); 44 | font-weight: var(--font-regular); 45 | } 46 | 47 | strong { 48 | font-weight: var(--font-medium); 49 | } 50 | 51 | pre, 52 | pre > *, 53 | pre > code * { 54 | font-family: var(--font-monospace) !important; 55 | } 56 | 57 | a { 58 | padding-bottom: 2px; 59 | text-decoration: none; 60 | display: inline-block; 61 | color: var(--color-main); 62 | font-weight: var(--font-medium); 63 | } 64 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_alert.css: -------------------------------------------------------------------------------- 1 | .alert { 2 | display: inline-flex; 3 | gap: 3rem; 4 | justify-content: space-between; 5 | align-items: center; 6 | padding: 1.5rem 2.5rem; 7 | border-radius: 3px; 8 | border: 1px solid var(--color-white-light); 9 | margin-bottom: 1rem; 10 | margin-right: 1rem; 11 | } 12 | 13 | .alert, 14 | .alert > .alert__message { 15 | background-color: var(--color-main-light); 16 | color: var(--color-main); 17 | font-size: 1.4rem; 18 | font-weight: var(--font-medium); 19 | } 20 | 21 | .alert.alert--error, 22 | .alert.alert--error > .alert__message { 23 | background-color: var(--color-error-bg); 24 | color: var(--color-error); 25 | } 26 | 27 | .alert.alert--success, 28 | .alert.alert--success > .alert__message { 29 | background-color: var(--color-success-bg); 30 | color: var(--color-success); 31 | } 32 | 33 | .alert > .alert__close { 34 | background: transparent; 35 | cursor: pointer; 36 | border: none; 37 | outline: transparent; 38 | font-size: 2rem; 39 | transition: var(--generic-transition); 40 | } 41 | 42 | .alert > .alert__close:hover { 43 | opacity: 0.85; 44 | } 45 | -------------------------------------------------------------------------------- /users/migrations/0006_auto_20210619_2142.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-06-19 16:42 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0005_alter_account_other_skills'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name='link', 16 | name='user', 17 | ), 18 | migrations.RemoveField( 19 | model_name='skill', 20 | name='user', 21 | ), 22 | migrations.AddField( 23 | model_name='link', 24 | name='account', 25 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='users.account'), 26 | preserve_default=False, 27 | ), 28 | migrations.AddField( 29 | model_name='skill', 30 | name='account', 31 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='users.account'), 32 | preserve_default=False, 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_grid.css: -------------------------------------------------------------------------------- 1 | .grid { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: flex-start; 5 | align-items: stretch; 6 | } 7 | 8 | .grid > .column { 9 | min-width: 28rem; 10 | margin-bottom: 2.5rem; 11 | } 12 | 13 | .grid--three > .column { 14 | flex-basis: 100%; 15 | } 16 | 17 | @media screen and (min-width: 800px) { 18 | .grid--three { 19 | margin-right: -2%; 20 | } 21 | 22 | .grid--three > .column { 23 | margin-right: 2%; 24 | } 25 | 26 | .grid--three > .column { 27 | flex-basis: 48%; 28 | } 29 | } 30 | 31 | @media screen and (min-width: 1200px) { 32 | .grid--three { 33 | margin-right: -2.33%; 34 | } 35 | 36 | .grid--three > .column { 37 | margin-right: 2.33%; 38 | } 39 | 40 | .grid--three > .column { 41 | flex-basis: 31%; 42 | } 43 | } 44 | 45 | .grid--two > .column { 46 | flex-basis: 100%; 47 | } 48 | 49 | @media screen and (min-width: 800px) { 50 | .grid--two { 51 | margin-right: -2%; 52 | } 53 | 54 | .grid--two > .column { 55 | margin-right: 2%; 56 | } 57 | 58 | .grid--two > .column { 59 | flex-basis: 48%; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_utilities.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 92%; 3 | max-width: 165rem; 4 | margin: 0 auto; 5 | box-sizing: border-box; 6 | } 7 | 8 | .container--narrow { 9 | width: 92%; 10 | max-width: 120rem; 11 | } 12 | 13 | @media screen and (min-width: 1000px) { 14 | .container { 15 | width: 90%; 16 | } 17 | 18 | .container--narrow { 19 | width: 85%; 20 | } 21 | } 22 | 23 | @media screen and (min-width: 1200px) { 24 | .container { 25 | width: 80%; 26 | } 27 | 28 | .container--narrow { 29 | width: 72%; 30 | } 31 | } 32 | 33 | @media screen and (min-width: 1440px) { 34 | .container { 35 | width: 72%; 36 | } 37 | 38 | .container--narrow { 39 | width: 65%; 40 | } 41 | } 42 | 43 | .text-center { 44 | text-align: center; 45 | } 46 | 47 | .m-sm { 48 | margin: 1rem; 49 | } 50 | 51 | .m-md { 52 | margin: 3rem; 53 | } 54 | 55 | .m-lg { 56 | margin: 5rem; 57 | } 58 | 59 | .m-xl { 60 | margin: 8rem; 61 | } 62 | 63 | .my-sm { 64 | margin: 1rem auto; 65 | } 66 | 67 | .my-md { 68 | margin: 3rem auto; 69 | } 70 | 71 | .my-lg { 72 | margin: 5rem auto; 73 | } 74 | 75 | .my-xl { 76 | margin: 8rem auto; 77 | } 78 | -------------------------------------------------------------------------------- /users/migrations/0003_auto_20210619_1325.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-06-19 08:25 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('users', '0002_auto_20210619_1313'), 13 | ] 14 | 15 | operations = [ 16 | migrations.RemoveField( 17 | model_name='account', 18 | name='links', 19 | ), 20 | migrations.RemoveField( 21 | model_name='account', 22 | name='skills', 23 | ), 24 | migrations.AddField( 25 | model_name='link', 26 | name='user', 27 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='auth.user'), 28 | preserve_default=False, 29 | ), 30 | migrations.AddField( 31 | model_name='skill', 32 | name='user', 33 | field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='auth.user'), 34 | preserve_default=False, 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /users/migrations/0004_auto_20210619_1511.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-06-19 10:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0003_auto_20210619_1325'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='account', 15 | name='about', 16 | field=models.TextField(default='Apparently, this user prefers to keep an air of mystery about them.', verbose_name='About'), 17 | ), 18 | migrations.AlterField( 19 | model_name='account', 20 | name='avatar', 21 | field=models.ImageField(default='/avatars/default.jpg', upload_to='avatars'), 22 | ), 23 | migrations.AlterField( 24 | model_name='account', 25 | name='location', 26 | field=models.TextField(default='Location is not specified.', verbose_name='Place of residence'), 27 | ), 28 | migrations.AlterField( 29 | model_name='account', 30 | name='summary', 31 | field=models.TextField(default='New user on our platform!', verbose_name='Summary about user'), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /main/migrations/0004_message.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-07-01 10:24 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('main', '0003_alter_project_user'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Message', 18 | fields=[ 19 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('subject', models.CharField(max_length=200, verbose_name='Message subject')), 21 | ('text', models.TextField(verbose_name='Message text')), 22 | ('is_read', models.BooleanField(default=False, verbose_name='Is read')), 23 | ('date', models.DateTimeField(auto_now_add=True)), 24 | ('user_from', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 25 | ('user_to', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_to', to=settings.AUTH_USER_MODEL)), 26 | ], 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /templates/reset_password_complete.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | DevSearch - Connect with Developers! 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |

Password reset complete

28 | 29 |

Your password has been set. You may go ahead and log in now.

30 |
31 | Login 32 |
33 |
34 |
35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_tag.css: -------------------------------------------------------------------------------- 1 | .tag { 2 | display: inline-flex; 3 | align-items: center; 4 | background-color: var(--color-sub); 5 | height: -webkit-fit-content; 6 | height: -moz-fit-content; 7 | height: fit-content; 8 | width: -webkit-fit-content; 9 | width: -moz-fit-content; 10 | width: fit-content; 11 | border-radius: 0.5rem; 12 | padding: 0.5rem 1.5rem; 13 | font-size: 1.2rem; 14 | cursor: pointer; 15 | } 16 | 17 | .tag:not(:last-child) { 18 | margin-right: 0.25rem; 19 | margin-bottom: 0.75rem; 20 | } 21 | 22 | .tag > small { 23 | color: var(--color-white); 24 | } 25 | 26 | .tag--outline > small { 27 | color: var(--color-sub); 28 | } 29 | 30 | .tag--outline { 31 | border: 2px solid var(--color-sub); 32 | background-color: transparent; 33 | padding: 0.4rem 1rem; 34 | } 35 | 36 | .tag--pill { 37 | border-radius: 5rem; 38 | } 39 | 40 | .tag--main, 41 | .tag--main > small { 42 | background: var(--color-main-light); 43 | color: var(--color-main); 44 | font-weight: 500; 45 | } 46 | 47 | .tag--sub, 48 | .tag--sub > small { 49 | background: var(--color-sub-lighter); 50 | color: var(--color-sub-light); 51 | font-weight: 500; 52 | } 53 | 54 | .tag--lg { 55 | padding: 0.8rem 3rem; 56 | font-size: 1.4rem; 57 | } 58 | 59 | .tag--lg:not(:last-child) { 60 | margin-right: 0.5rem; 61 | margin-bottom: 1rem; 62 | } 63 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_loading.css: -------------------------------------------------------------------------------- 1 | .loading { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | flex-direction: column; 6 | position: fixed; 7 | top: 0; 8 | left: 0; 9 | right: 0; 10 | bottom: 0; 11 | background-color: var(--color-bg); 12 | } 13 | 14 | .loading__loader { 15 | display: inline-block; 16 | font-size: 0; 17 | padding: 0; 18 | } 19 | 20 | .loading__loader span { 21 | vertical-align: middle; 22 | border-radius: 100%; 23 | display: inline-block; 24 | width: 2rem; 25 | height: 2rem; 26 | margin: 0.5rem; 27 | -webkit-animation: loading 0.8s linear infinite alternate; 28 | animation: loading 0.8s linear infinite alternate; 29 | background-color: var(--color-main); 30 | } 31 | 32 | .loading__loader span:nth-child(1) { 33 | -webkit-animation-delay: -1s; 34 | animation-delay: -1s; 35 | opacity: 0.6; 36 | } 37 | 38 | .loading__loader span:nth-child(2) { 39 | -webkit-animation-delay: -0.8s; 40 | animation-delay: -0.8s; 41 | opacity: 0.8; 42 | } 43 | 44 | .loading__loader span:nth-child(3) { 45 | -webkit-animation-delay: -0.26666s; 46 | animation-delay: -0.26666s; 47 | opacity: 1; 48 | } 49 | 50 | .loading__loader span:nth-child(4) { 51 | -webkit-animation-delay: -0.8s; 52 | animation-delay: -0.8s; 53 | opacity: 0.8; 54 | } 55 | 56 | .loading__loader span:nth-child(5) { 57 | -webkit-animation-delay: -1s; 58 | animation-delay: -1s; 59 | opacity: 0.4; 60 | } 61 | -------------------------------------------------------------------------------- /templates/reset_password_sent.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | DevSearch - Connect with Developers! 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |

Password reset sent

28 | 29 |

We’ve emailed you instructions for setting your password, if an account exists with the email you 30 | entered. You should receive them shortly.

31 |
32 |

If you don’t receive an email, please make sure you’ve entered the address you registered with, and check 33 | your spam folder.

34 |
35 |
36 |
37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /users/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.postgres.fields import ArrayField 3 | 4 | from django.contrib.auth.models import User 5 | 6 | 7 | class Account(models.Model): 8 | user = models.OneToOneField(User, on_delete=models.CASCADE) 9 | avatar = models.ImageField(default='/avatars/default.jpg', upload_to='avatars') 10 | summary = models.TextField('Summary about user', default='New user on our platform!') 11 | location = models.TextField('Place of residence', default='Location is not specified.') 12 | about = models.TextField('About', default='Apparently, this user prefers to keep an air of mystery about them.') 13 | other_skills = ArrayField(models.CharField('Other skills', max_length=30), null=True) 14 | 15 | def __str__(self): 16 | return f'{self.user.username} Account' 17 | 18 | 19 | class Skill(models.Model): 20 | account = models.ForeignKey(Account, on_delete=models.CASCADE) 21 | name = models.CharField('Skill name', max_length=20) 22 | description = models.TextField('Skill description') 23 | 24 | def __str__(self): 25 | return f'{self.account} - {self.name} Skill' 26 | 27 | 28 | class Link(models.Model): 29 | ICONS = ( 30 | ('im im-github', 'GitHub'), 31 | ('im im-stackoverflow', 'StackOverflow'), 32 | ('im im-linkedin', 'LinkedIn'), 33 | ('im im-twitter', 'Twitter'), 34 | ('im im-globe', 'Website') 35 | ) 36 | account = models.ForeignKey(Account, on_delete=models.CASCADE) 37 | name = models.CharField('Link name', max_length=30) 38 | link = models.URLField('URL') 39 | icon = models.TextField('Icon name', choices=ICONS) 40 | 41 | def __str__(self): 42 | return f'{self.name} Link' 43 | -------------------------------------------------------------------------------- /main/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.postgres.fields import ArrayField 3 | 4 | from django.contrib.auth.models import User 5 | 6 | 7 | class Project(models.Model): 8 | user = models.ForeignKey(User, on_delete=models.CASCADE) 9 | title = models.CharField('Title', max_length=120) 10 | description = models.TextField('Description') 11 | tags = ArrayField(models.CharField('Tags', max_length=30), null=True) 12 | link = models.URLField('Source code link') 13 | image = models.ImageField('Project image', upload_to='project_images') 14 | date = models.DateField('Date of creation', auto_now_add=True) 15 | 16 | def __str__(self): 17 | return f'{self.user} - {self.title} Project' 18 | 19 | @property 20 | def feedbackCount(self): 21 | return f'{self.comment_set.count()} feedback(s)' 22 | 23 | 24 | class Comment(models.Model): 25 | author = models.ForeignKey(User, on_delete=models.CASCADE) 26 | project = models.ForeignKey(Project, on_delete=models.CASCADE) 27 | text = models.TextField('Text of comment') 28 | date = models.DateField('Date', auto_now_add=True) 29 | 30 | def __str__(self): 31 | return f'{self.author} Comment to {self.project}' 32 | 33 | 34 | class Message(models.Model): 35 | user_from = models.ForeignKey(User, on_delete=models.CASCADE) 36 | user_to = models.ForeignKey(User, related_name='user_to', on_delete=models.CASCADE) 37 | subject = models.CharField('Message subject', max_length=200) 38 | text = models.TextField('Message text') 39 | is_read = models.BooleanField('Is read', default=False) 40 | date = models.DateTimeField(auto_now_add=True) 41 | 42 | def __str__(self): 43 | return f'Message from {self.user_from} to {self.user_to}' 44 | -------------------------------------------------------------------------------- /devSearch/urls.py: -------------------------------------------------------------------------------- 1 | """devSearch URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls.py import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls.py')) 15 | """ 16 | from django.contrib import admin 17 | 18 | from django.urls import path, include 19 | 20 | from django.conf import settings 21 | from django.conf.urls.static import static 22 | 23 | from django.contrib.auth import views as auth_views 24 | 25 | 26 | urlpatterns = [ 27 | path('admin/', admin.site.urls), 28 | path('', include('main.urls')), 29 | path('users/', include('users.urls')), 30 | 31 | path('reset_password/', auth_views.PasswordResetView.as_view(template_name="reset_password.html"), 32 | name="reset_password"), 33 | path('reset_password_sent/', auth_views.PasswordResetDoneView.as_view(template_name="reset_password_sent.html"), 34 | name="password_reset_done"), 35 | 36 | path('reset///', auth_views.PasswordResetConfirmView.as_view(template_name="reset.html"), 37 | name="password_reset_confirm"), 38 | 39 | path('reset_password_complete/', 40 | auth_views.PasswordResetCompleteView.as_view(template_name="reset_password_complete.html"), 41 | name="password_reset_complete"), 42 | ] 43 | 44 | if settings.DEBUG: 45 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 46 | -------------------------------------------------------------------------------- /templates/forgetpassword.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | DevSearch - Connect with Developers! 19 | 20 | 21 | 22 |
23 |
24 |
25 | 26 | DevSearch Logo 27 | 28 |

Forget Password?

29 |

Reset password by entering the email address

30 |
31 | 32 |
33 | 34 |
35 | 36 | 43 |
44 | 45 |
46 | 47 | Login 48 |
49 |
50 |
51 |

Don’t have an Account?

52 | Sign Up 53 |
54 |
55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /main/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-06-30 15:53 2 | 3 | from django.conf import settings 4 | import django.contrib.postgres.fields 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Project', 20 | fields=[ 21 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('title', models.CharField(max_length=120, verbose_name='Title')), 23 | ('description', models.TextField(verbose_name='Description')), 24 | ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=30, verbose_name='Tags'), null=True, size=None)), 25 | ('link', models.URLField(verbose_name='Source code link')), 26 | ('image', models.ImageField(upload_to='project_images', verbose_name='Project image')), 27 | ('date', models.DateField(auto_now_add=True, verbose_name='Date of creation')), 28 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 29 | ], 30 | ), 31 | migrations.CreateModel( 32 | name='Comment', 33 | fields=[ 34 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 35 | ('text', models.TextField(verbose_name='Text of comment')), 36 | ('date', models.DateField(auto_now_add=True, verbose_name='Date')), 37 | ('author', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 38 | ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.project')), 39 | ], 40 | ), 41 | ] 42 | -------------------------------------------------------------------------------- /static/uikit/app.css: -------------------------------------------------------------------------------- 1 | .mumble-block { 2 | padding: 4.2rem 0; 3 | } 4 | 5 | .mumble-block__title { 6 | color: var(--color-main); 7 | font-size: 3rem; 8 | font-weight: 700; 9 | } 10 | 11 | .mumble-block__info { 12 | padding-bottom: 1rem; 13 | border-bottom: 2px solid var(--color-main); 14 | } 15 | 16 | .mumble-block__preview { 17 | padding: 3rem 0; 18 | } 19 | 20 | .mumble-block--header { 21 | background-color: var(--color-light); 22 | } 23 | 24 | .mumble-block--typography .mumble-block__preview > * { 25 | line-height: 2; 26 | } 27 | 28 | .mumble-block--card .mumble-block__preview { 29 | display: flex; 30 | } 31 | 32 | .mumble-block--card .mumble-block__preview > * { 33 | margin-right: 2rem; 34 | } 35 | 36 | .mumble-block--avatar .mumble-block__preview > * { 37 | margin-right: 1rem; 38 | } 39 | 40 | code { 41 | border-radius: 0.5rem; 42 | padding: 1rem 2rem; 43 | background-color: var(--color-sub); 44 | color: var(--color-white); 45 | } 46 | 47 | pre { 48 | display: inline-block; 49 | padding: 0 1rem; 50 | border-radius: 3px; 51 | background: var(--color-main-light); 52 | } 53 | 54 | pre.codeblock { 55 | display: block; 56 | padding: 0; 57 | border-radius: 0; 58 | background: transparent; 59 | } 60 | 61 | .codeblock code { 62 | border-radius: 0.5rem; 63 | height: auto; 64 | } 65 | 66 | .codeblock--sm { 67 | height: 24rem; 68 | } 69 | 70 | .codeblock--md { 71 | height: 32rem; 72 | } 73 | 74 | .codeblock--lg { 75 | height: 44rem; 76 | } 77 | 78 | .codeblock--xl { 79 | height: 64rem; 80 | } 81 | 82 | .mumble-block__preview { 83 | position: relative; 84 | } 85 | 86 | .mumble-block__preview > .loading { 87 | margin-bottom: 24rem; 88 | position: absolute; 89 | height: 20rem; 90 | } 91 | 92 | .codeblock--loading { 93 | margin-top: 12rem; 94 | } 95 | 96 | .layout { 97 | background: var(--color-main-light); 98 | } 99 | .layout > .column, 100 | .grid > .column { 101 | color: var(--color-light); 102 | text-align: center; 103 | background: var(--color-main); 104 | } 105 | -------------------------------------------------------------------------------- /templates/reset.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | DevSearch - Connect with Developers! 19 | 20 | 21 | 22 | 23 | 46 | 47 | 48 | 49 |
50 |
51 |
52 |

Enter new password

53 | 54 |

Please enter your new password twice so we can verify you typed it in correctly.

55 | 56 |
57 | 58 |
59 | {% csrf_token %} 60 | {{ form.as_p }} 61 | 62 |
63 |
64 |
65 |
66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /templates/reset_password.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | DevSearch - Connect with Developers! 19 | 20 | 21 | 22 | 23 | 46 | 47 | 48 | 49 |
50 |
51 |
52 |

Password reset

53 | 54 |

Forgotten your password? Enter your email address below, and we’ll email instructions for setting a new 55 | one.

56 | 57 |
58 | 59 |
60 | {% csrf_token %} 61 | {{ form.as_p }} 62 | 63 |
64 |
65 |
66 |
67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :computer: DevSearch - Connect With Developers From Around The World! 2 | 3 | ### Technologies Stack 4 | - Django 5 | - PostgreSQL 6 | - HTML / CSS 7 | 8 | ### Developer Staff 9 | - [h4cktivist](https://github.com/h4cktivist) 10 | - Templates for frontend were taken from [divanov11/Django-2021](https://github.com/divanov11/Django-2021) 11 | 12 | ### Website Features 13 | - Browse and search for developers 14 | - Browse and search for projects 15 | - Sign up and log in into account 16 | - Edit / Delete account information 17 | - Create / Edit / Delete your projects 18 | - Comment other's projects 19 | - Send messages to developers / Read your inbox messages 20 | - Reset password to your account via email 21 | 22 | ### Preview 23 | ![1](https://user-images.githubusercontent.com/51692800/124136602-5d4dcd80-da9e-11eb-8bbe-0192a989251f.png) 24 | ![2](https://user-images.githubusercontent.com/51692800/124136514-49a26700-da9e-11eb-93b8-9a319f332b97.png) 25 | ![4](https://user-images.githubusercontent.com/51692800/124136520-4a3afd80-da9e-11eb-8013-a9503aa84937.png) 26 | ![5](https://user-images.githubusercontent.com/51692800/124136524-4ad39400-da9e-11eb-9d42-fbab06202659.png) 27 | ![7](https://user-images.githubusercontent.com/51692800/124136630-6179eb00-da9e-11eb-8ffd-4cc889043356.png) 28 | ![6](https://user-images.githubusercontent.com/51692800/124136526-4ad39400-da9e-11eb-9910-e36f4266138b.png) 29 | 30 | ### Run it yourself 31 | ```sh 32 | git clone https://github.com/h4cktivist/devSearch.git 33 | cd devSearch 34 | pip install - r requirements.txt 35 | ``` 36 | 37 | Go to the `setting.py` and change this lines up to your PostgreSQL account 38 | ```python 39 | DATABASES = { 40 | 'default': { 41 | 'ENGINE': 'django.db.backends.postgresql', 42 | 'NAME': 'devSearch', 43 | 'HOST': 'localhost', 44 | 'PORT': '5432', 45 | 'USER': 'postgres', 46 | 'PASSWORD': ENV['DB_PASS'] 47 | } 48 | } 49 | ``` 50 | Then run the migrations: 51 | ```sh 52 | python manage.py migrate 53 | ``` 54 | 55 | Also change this up to your email for reset password confirmation feature 56 | ```python 57 | EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' 58 | EMAIL_HOST = 'smtp.gmail.com' 59 | EMAIL_PORT = 587 60 | EMAIL_USE_TLS = True 61 | EMAIL_HOST_USER = ENV['EMAIL'] 62 | EMAIL_HOST_PASSWORD = ENV['EMAIL_PASS'] 63 | ``` 64 | 65 | Then you can run it 66 | ```sh 67 | python manage.py runserver 68 | ``` 69 | -------------------------------------------------------------------------------- /users/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.4 on 2021-06-19 07:47 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Link', 19 | fields=[ 20 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(max_length=30, verbose_name='Link name')), 22 | ('link', models.URLField(verbose_name='URL')), 23 | ('icon', models.TextField(choices=[('im im-github', 'GitHub'), ('im im-stackoverflow', 'StackOverflow'), ('im im-linkedin', 'LinkedIn'), ('im im-twitter', 'Twitter'), ('im im-globe', 'Website')], verbose_name='Icon name')), 24 | ], 25 | ), 26 | migrations.CreateModel( 27 | name='Skill', 28 | fields=[ 29 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 30 | ('name', models.CharField(max_length=20, verbose_name='Skill name')), 31 | ('description', models.TextField(verbose_name='Skill description')), 32 | ], 33 | ), 34 | migrations.CreateModel( 35 | name='Account', 36 | fields=[ 37 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 38 | ('avatar', models.ImageField(default='default.jpg', upload_to='avatars')), 39 | ('summary', models.TextField(verbose_name='Summary about user')), 40 | ('location', models.TextField(verbose_name='Place of residence')), 41 | ('about', models.TextField(verbose_name='About')), 42 | ('other_skills', models.TextField(verbose_name='Other skills')), 43 | ('links', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='Links', to='users.link')), 44 | ('skills', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='Skills', to='users.skill')), 45 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 46 | ], 47 | ), 48 | ] 49 | -------------------------------------------------------------------------------- /templates/login.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | DevSearch - Connect with Developers! 19 | 20 | 21 | 22 |
23 |
24 |
25 | 26 | icon 27 | 28 |

Account Login

29 |

Hello Developer, Welcome Back!

30 |
31 | 32 |
33 | {% csrf_token %} 34 | 35 |
36 | 37 | 44 |
45 | 46 | 47 |
48 | 49 | 56 |
57 |
58 | 59 | Forget Password? 60 |
61 | {% for msg in messages %} 62 |

{{ msg }}

63 | {% endfor %} 64 |
65 |
66 |

Don’t have an Account?

67 | Sign Up 68 |
69 |
70 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /templates/message.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | DevSearch - Connect with Developers! 20 | 21 | 22 | 23 | 24 |
25 |
26 | 29 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 | 55 |

{{ message.subject }}

56 | {{ message.user_from.first_name }} {{ message.user_from.last_name }} 57 |

{{ message.date }}

58 |
59 | {{ message.text }} 60 |
61 |
62 |
63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_button.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | display: inline-block; 3 | position: relative; 4 | cursor: pointer; 5 | background-color: var(--color-light); 6 | font-weight: var(--font-medium); 7 | color: var(--color-sub); 8 | text-decoration: none; 9 | padding: 1rem 2rem; 10 | border-radius: 0.5rem; 11 | border: none; 12 | width: -webkit-fit-content; 13 | width: -moz-fit-content; 14 | width: fit-content; 15 | height: -webkit-fit-content; 16 | height: -moz-fit-content; 17 | height: fit-content; 18 | font-size: 1.6rem; 19 | outline-width: 0; 20 | outline-color: transparent; 21 | -webkit-backface-visibility: hidden; 22 | backface-visibility: hidden; 23 | transition: all 0.3s ease-in-out; 24 | } 25 | 26 | .btn:disabled, 27 | .btn--disabled { 28 | cursor: not-allowed; 29 | opacity: 0.6; 30 | } 31 | 32 | .btn:hover { 33 | opacity: 0.85; 34 | } 35 | 36 | .btn i { 37 | pointer-events: none !important; 38 | } 39 | 40 | .btn--main { 41 | background-color: var(--color-main); 42 | color: var(--color-white); 43 | } 44 | 45 | .btn.btn--main--outline { 46 | background-color: transparent; 47 | color: var(--color-main); 48 | border: 2px solid var(--color-main); 49 | } 50 | 51 | .btn--sub { 52 | background-color: var(--color-sub); 53 | color: var(--color-white); 54 | } 55 | 56 | .btn.btn--sub--outline { 57 | background-color: transparent; 58 | color: var(--color-sub); 59 | border: 2px solid var(--color-sub); 60 | } 61 | 62 | .btn--sm { 63 | font-size: 1.3rem; 64 | padding: 0.5rem 1.2rem; 65 | } 66 | 67 | .btn--md { 68 | font-size: 1.35rem; 69 | padding: 0.8rem 2rem; 70 | } 71 | 72 | .btn--lg { 73 | font-size: 1.8rem; 74 | padding: 1.2rem 4.5rem; 75 | } 76 | 77 | .btn--main--link, 78 | .btn--sub--link { 79 | padding: 0; 80 | background: transparent; 81 | } 82 | 83 | .btn.btn--main--link { 84 | color: var(--color-main); 85 | } 86 | 87 | .btn.btn--main--link i { 88 | color: var(--color-main) !important; 89 | } 90 | 91 | .btn.btn--sub--link { 92 | color: var(--color-sub); 93 | } 94 | 95 | .btn.btn--sub--link i { 96 | color: var(--color-sub) !important; 97 | } 98 | 99 | .btn--main--outline:hover { 100 | opacity: 1; 101 | color: var(--color-white); 102 | background: var(--color-main); 103 | } 104 | 105 | .btn--sub--outline:hover { 106 | opacity: 1; 107 | color: var(--color-white); 108 | background: var(--color-sub); 109 | } 110 | 111 | .btn.button--spinner { 112 | border-radius: 50%; 113 | width: 24px; 114 | height: 24px; 115 | animation: spin 2s linear infinite; 116 | } 117 | 118 | button:disabled, 119 | button[disabled] { 120 | border: 1px solid var(--color-gray); 121 | background-color: var(--color-light); 122 | color: var(--color-gray); 123 | cursor: unset; 124 | } 125 | -------------------------------------------------------------------------------- /main/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponseForbidden 2 | from django.shortcuts import render, redirect 3 | from django.core.paginator import Paginator 4 | 5 | from django.contrib.auth.models import User 6 | from django.db.models import Q 7 | 8 | from django.contrib.auth.decorators import login_required 9 | 10 | from .models import Project, Message 11 | 12 | 13 | def index(request): 14 | if request.method == 'POST': 15 | search_data = request.POST.get('search') 16 | users = User.objects.filter(Q(first_name__contains=search_data) | Q(last_name__contains=search_data))\ 17 | .order_by('-date_joined') 18 | else: 19 | users = User.objects.order_by('-date_joined') 20 | 21 | paginator = Paginator(users, 6) 22 | page = request.GET.get('page', 1) 23 | page_users = paginator.get_page(page) 24 | 25 | return render(request, 'index.html', {'users': page_users}) 26 | 27 | 28 | def projects(request): 29 | if request.method == 'POST': 30 | search_data = request.POST.get('search') 31 | projects = Project.objects.filter(title__contains=search_data).order_by('-date') 32 | else: 33 | projects = Project.objects.order_by('-date') 34 | 35 | paginator = Paginator(projects, 6) 36 | page = request.GET.get('page', 1) 37 | page_projects = paginator.get_page(page) 38 | 39 | return render(request, 'projects.html', {'projects': page_projects}) 40 | 41 | 42 | def singleProject(request, id): 43 | project = Project.objects.get(id=id) 44 | if request.method == 'POST': 45 | project.comment_set.create( 46 | author=request.user, 47 | text=request.POST.get('message') 48 | ) 49 | return redirect('single-project', project.id) 50 | 51 | return render(request, 'single-project.html', {'project': project}) 52 | 53 | 54 | @login_required(login_url='login') 55 | def inbox(request): 56 | messages = Message.objects.filter(user_to=request.user).order_by('-date') 57 | unread = messages.filter(is_read=False).count() 58 | return render(request, 'inbox.html', {'messages': messages, 'unread': unread}) 59 | 60 | 61 | @login_required(login_url='login') 62 | def singleMessage(request, id): 63 | message = Message.objects.get(id=id) 64 | if message.user_to == request.user: 65 | message.is_read = True 66 | message.save() 67 | return render(request, 'message.html', {'message': message}) 68 | 69 | return HttpResponseForbidden 70 | 71 | 72 | @login_required(login_url='login') 73 | def sendMessage(request, user_id): 74 | user_to = User.objects.get(id=user_id) 75 | if request.method == 'POST': 76 | message = Message( 77 | user_to=user_to, 78 | user_from=request.user, 79 | subject=request.POST.get('subject'), 80 | text=request.POST.get('text') 81 | ) 82 | message.save() 83 | return redirect('profile', user_to.username) 84 | 85 | return render(request, 'send-message.html', {'user_to': user_to}) 86 | -------------------------------------------------------------------------------- /templates/inbox.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | DevSearch - Connect with Developers! 20 | 21 | 22 | 23 | 24 |
25 |
26 | 29 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |

New Messages({{ unread }})

54 | 69 |
70 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /templates/delete.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | DevSearch - Connect with Developers! 20 | 21 | 22 | 23 | 24 |
25 |
26 | 29 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 | 55 |
56 | 57 |
58 | {% csrf_token %} 59 |

{{ warning }}

60 | ← Go Back 61 | 62 |
63 |
64 |
65 |
66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /templates/signup.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | DevSearch - Connect with Developers! 19 | 20 | 21 | 22 |
23 |
24 |
25 | 26 | icon 27 | 28 |

Account SignUp

29 |

Create a new developer account

30 |
31 | 32 |
33 | {% csrf_token %} 34 | 35 |
36 | 37 | 45 |
46 | 47 | 48 |
49 | 50 | 58 |
59 | 60 | 61 |
62 | 63 | 71 |
72 | 73 |
74 | 75 | 83 |
84 |
85 | 86 |
87 | {% for msg in messages %} 88 |

{{ msg }}

89 | {% endfor %} 90 |
91 |
92 |

Already have an Account?

93 | Log In 94 |
95 |
96 |
97 | 98 | 99 | -------------------------------------------------------------------------------- /templates/send-message.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | DevSearch - Connect with Developers! 20 | 21 | 22 | 23 | 24 |
25 |
26 | 29 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 | 55 |
56 | 57 |
58 | {% csrf_token %} 59 | 60 |
61 | 62 | 64 |
65 | 66 | 67 |
68 | 69 | 70 |
71 | 72 | 73 |
74 |
75 |
76 |
77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /templates/skill-add-edit-form.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | DevSearch - Connect with Developers! 20 | 21 | 22 | 23 | 24 |
25 |
26 | 29 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 | 55 |
56 | 57 |
58 | {% csrf_token %} 59 | 60 |
61 | 62 | 64 |
65 | 66 | 67 |
68 | 69 | 71 |
72 | 73 | 74 |
75 |
76 |
77 |
78 | 79 | 80 | -------------------------------------------------------------------------------- /static/uikit/styles/modules/_form.css: -------------------------------------------------------------------------------- 1 | .form { 2 | display: block; 3 | } 4 | 5 | .form--inline { 6 | display: inline-block; 7 | } 8 | 9 | .form__field { 10 | margin-top: 1.5rem; 11 | margin-bottom: 1.5rem; 12 | } 13 | 14 | .form__error { 15 | color: var(--color-error); 16 | font-weight: var(--font-medium); 17 | display: block; 18 | font-size: 1.4rem; 19 | } 20 | 21 | .form__label, 22 | .form__field > label { 23 | display: block; 24 | margin-bottom: 0.5rem; 25 | color: var(--color-text); 26 | } 27 | 28 | .form__label--inline { 29 | display: inline-block; 30 | margin-bottom: 0.8rem; 31 | color: var(--color-text); 32 | } 33 | 34 | .form__label--hidden { 35 | position: absolute; 36 | text-indent: -9999px; 37 | } 38 | 39 | input.input[type='text'], 40 | input.input[type='email'], 41 | input.input[type='password'], 42 | input.input[type='number'], 43 | textarea.input { 44 | min-width: 24rem; 45 | width: 100%; 46 | padding: 1.2rem 1.5rem; 47 | border-radius: 0.5rem; 48 | border: 2px solid var(--color-light); 49 | transition: all 0.3s ease-in-out; 50 | background-color: var(--color-bg); 51 | } 52 | 53 | input.input[type='text']:focus, 54 | input.input[type='email']:focus, 55 | input.input[type='password']:focus, 56 | input.input[type='number']:focus, 57 | input.input[type='text']:active, 58 | input.input[type='email']:active, 59 | input.input[type='password']:active, 60 | input.input[type='number']:active, 61 | select.input, 62 | textarea.input:focus, 63 | textarea.input:active { 64 | outline: none; 65 | border: 2px solid var(--color-main); 66 | } 67 | 68 | .form__field--radio, 69 | .form__field--checkbox { 70 | display: flex; 71 | align-items: center; 72 | } 73 | 74 | .form__field--radio p, 75 | .form__field--checkbox p { 76 | margin-right: 3rem; 77 | font-size: 1.5rem; 78 | font-weight: 400; 79 | color: var(--color-text); 80 | } 81 | 82 | .form__field--radio input + label, 83 | .form__field--checkbox input + label { 84 | display: inline-block; 85 | margin-left: 1.5rem; 86 | margin-right: 2rem; 87 | margin-bottom: 0; 88 | cursor: pointer; 89 | color: var(--color-text); 90 | } 91 | 92 | .input--textarea { 93 | width: 100%; 94 | min-height: 14rem; 95 | resize: none; 96 | } 97 | 98 | .input--textarea--sm { 99 | min-height: 8rem; 100 | } 101 | 102 | /* Custom Radio & Checkbox */ 103 | 104 | .input.input--checkbox, 105 | .input.input--radio { 106 | position: relative; 107 | cursor: pointer; 108 | } 109 | 110 | .input.input--checkbox::before { 111 | transition: transform 0.4s cubic-bezier(0.45, 1.8, 0.5, 0.75); 112 | transform: rotate(-45deg) scale(0, 0); 113 | content: ''; 114 | position: absolute; 115 | left: 4px; 116 | top: 1.4px; 117 | z-index: 1; 118 | width: 0.8rem; 119 | height: 0.4rem; 120 | border: 2.5px solid var(--color-main); 121 | border-top-style: none; 122 | border-right-style: none; 123 | } 124 | 125 | .input.input--checkbox:checked::before { 126 | transform: rotate(-45deg) scale(1, 1); 127 | } 128 | 129 | .input.input--checkbox::after { 130 | content: ''; 131 | position: absolute; 132 | left: 0; 133 | bottom: -3px; 134 | width: 1.5rem; 135 | height: 1.5rem; 136 | background: var(--color-white); 137 | border: 2px solid var(--color-gray); 138 | cursor: pointer; 139 | } 140 | 141 | .input.input--checkbox:checked::after { 142 | background: var(--color-main-light); 143 | border-color: var(--color-main); 144 | } 145 | 146 | .input.input--radio::before { 147 | transition: transform 0.4s cubic-bezier(0.45, 1.8, 0.5, 0.75); 148 | transform: scale(0, 0); 149 | content: ''; 150 | position: absolute; 151 | left: 3.5px; 152 | top: 2.5px; 153 | z-index: 1; 154 | width: 0.9rem; 155 | height: 0.9rem; 156 | background: var(--color-main); 157 | border-radius: 50%; 158 | } 159 | 160 | .input.input--radio:checked::before { 161 | transform: scale(1, 1); 162 | } 163 | 164 | .input.input--radio::after { 165 | content: ''; 166 | position: absolute; 167 | top: -0.25rem; 168 | left: -0.125rem; 169 | width: 1.5rem; 170 | height: 1.5rem; 171 | background: var(--color-white); 172 | border: 2px solid var(--color-gray); 173 | border-radius: 50%; 174 | } 175 | 176 | .input.input--radio:checked::after { 177 | background: var(--color-main-light); 178 | } 179 | 180 | .form__field--action > *:not(:last-child) { 181 | margin-right: 1rem; 182 | } 183 | -------------------------------------------------------------------------------- /devSearch/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for devSearch project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | from dotenv import dotenv_values 16 | 17 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 18 | BASE_DIR = Path(__file__).resolve().parent.parent 19 | 20 | ENV = dotenv_values('.env') 21 | 22 | # Quick-start development settings - unsuitable for production 23 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 24 | 25 | # SECURITY WARNING: keep the secret key used in production secret! 26 | SECRET_KEY = 'django-insecure-)06m1o(breobcik6_t1jn$%b0&oxv!a8_=2(6ra4fix0@ep-=m' 27 | 28 | # SECURITY WARNING: don't run with debug turned on in production! 29 | DEBUG = True 30 | 31 | ALLOWED_HOSTS = [] 32 | 33 | 34 | # Application definition 35 | 36 | INSTALLED_APPS = [ 37 | 'django.contrib.admin', 38 | 'django.contrib.auth', 39 | 'django.contrib.contenttypes', 40 | 'django.contrib.sessions', 41 | 'django.contrib.messages', 42 | 'django.contrib.staticfiles', 43 | 'main', 44 | 'users', 45 | ] 46 | 47 | MIDDLEWARE = [ 48 | 'django.middleware.security.SecurityMiddleware', 49 | 'django.contrib.sessions.middleware.SessionMiddleware', 50 | 'django.middleware.common.CommonMiddleware', 51 | 'django.middleware.csrf.CsrfViewMiddleware', 52 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 53 | 'django.contrib.messages.middleware.MessageMiddleware', 54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 55 | ] 56 | 57 | ROOT_URLCONF = 'devSearch.urls' 58 | 59 | TEMPLATES = [ 60 | { 61 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 62 | 'DIRS': [BASE_DIR / 'templates'] 63 | , 64 | 'APP_DIRS': True, 65 | 'OPTIONS': { 66 | 'context_processors': [ 67 | 'django.template.context_processors.debug', 68 | 'django.template.context_processors.request', 69 | 'django.contrib.auth.context_processors.auth', 70 | 'django.contrib.messages.context_processors.messages', 71 | ], 72 | }, 73 | }, 74 | ] 75 | 76 | WSGI_APPLICATION = 'devSearch.wsgi.application' 77 | 78 | 79 | # Database 80 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 81 | 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.postgresql', 85 | 'NAME': 'devSearch', 86 | 'HOST': 'localhost', 87 | 'PORT': '5432', 88 | 'USER': 'postgres', 89 | 'PASSWORD': ENV['DB_PASS'] 90 | } 91 | } 92 | 93 | 94 | # Password validation 95 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 96 | 97 | AUTH_PASSWORD_VALIDATORS = [ 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 100 | }, 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 106 | }, 107 | { 108 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 109 | }, 110 | ] 111 | 112 | 113 | # Internationalization 114 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 115 | 116 | LANGUAGE_CODE = 'en-us' 117 | 118 | TIME_ZONE = 'UTC' 119 | 120 | USE_I18N = True 121 | 122 | USE_L10N = True 123 | 124 | USE_TZ = True 125 | 126 | 127 | EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' 128 | EMAIL_HOST = 'smtp.gmail.com' 129 | EMAIL_PORT = 587 130 | EMAIL_USE_TLS = True 131 | EMAIL_HOST_USER = ENV['EMAIL'] 132 | EMAIL_HOST_PASSWORD = ENV['EMAIL_PASS'] 133 | 134 | # Static files (CSS, JavaScript, Images) 135 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 136 | 137 | STATIC_URL = '/static/' 138 | 139 | STATICFILES_DIRS = [ 140 | BASE_DIR / 'static', 141 | ] 142 | 143 | 144 | MEDIA_ROOT = BASE_DIR / 'media' 145 | MEDIA_URL = '/media/' 146 | 147 | # Default primary key field type 148 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 149 | 150 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 151 | -------------------------------------------------------------------------------- /templates/project-add-edit-form.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | DevSearch - Connect with Developers! 20 | 21 | 22 | 23 | 24 |
25 |
26 | 29 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 | 55 |
56 | 57 |
58 | {% csrf_token %} 59 | 60 |
61 | 62 | 64 |
65 | 66 |
67 | 68 | 69 |
70 | 71 |
72 | 73 | 75 |
76 | 77 |
78 | 79 | 82 |
83 | 84 |
85 | 86 | 87 |
88 | {% for msg in messages %} 89 |

{{ msg }}

90 | {% endfor %} 91 | 92 |
93 |
94 |
95 |
96 | 97 | 98 | -------------------------------------------------------------------------------- /templates/single-project.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | DevSearch - Connect with Developers! 20 | 21 | 22 | 23 | 24 |
25 |
26 | 29 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 |
55 |

Tools & Stacks

56 |
57 | {% for tag in project.tags %} 58 | 59 | {{ tag }} 60 | 61 | {% endfor %} 62 |
63 | Source Code 64 | 65 |
66 |
67 | portfolio thumbnail 68 | {{ project.user.first_name }} {{ project.user.last_name }} 69 |

{{ project.title }}

70 |

About the Project

71 |
72 | {{ project.description }} 73 |
74 | 75 |
76 |

Feedbacks

77 | 78 | {% if not request.user.is_authenticated %} 79 | Please login to leave a review 80 | {% elif request.user == project.user %} 81 |

You cannot comment your own work

82 | {% else %} 83 |
84 | {% csrf_token %} 85 | 86 |
87 | 88 | 90 |
91 | 92 |
93 | {% endif %} 94 | 95 |
96 | {% for comment in project.comment_set.all %} 97 |
98 | 99 | user 100 | 101 |
102 | {{ comment.author.first_name }} {{ comment.author.last_name }} 103 |

104 | {{ comment.text }} 105 |

106 |
107 |
108 | {% endfor %} 109 |
110 |
111 |
112 |
113 |
114 | 115 |
116 | 117 | 118 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | DevSearch - Connect with Developers! 21 | 22 | 23 | 24 | 25 |
26 |
27 | 30 | 48 |
49 |
50 | 51 | 52 |
53 |
54 |
55 |
56 |

CONNECT WITH DEVELOPERS

57 |

FROM AROUND THE WORLD

58 |
59 | 60 | 72 |
73 |
74 | 75 |
76 | 104 |
105 | 106 | 129 |
130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /templates/account-edit-form.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | DevSearch - Connect with Developers! 20 | 21 | 22 | 23 | 24 |
25 |
26 | 29 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 | 55 |
56 | 57 |
58 | {% csrf_token %} 59 | 60 |
61 | 62 | 64 |
65 | 66 |
67 | 68 | 70 |
71 | 72 | 73 |
74 | 75 | 77 |
78 | 79 | 80 |
81 | 82 | 84 |
85 | 86 |
87 | 88 | 89 |
90 | 91 |
92 | 93 | 95 |
96 | 97 |
98 | 99 | 100 |
101 | {% for msg in messages %} 102 |

{{ msg }}

103 | {% endfor %} 104 | 105 |
106 |
107 |
108 |
109 | 110 | 111 | -------------------------------------------------------------------------------- /templates/projects.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | DevSearch - Connect with Developers! 20 | 21 | 22 | 23 | 24 |
25 |
26 | 29 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 |
55 |

Search for Projects

56 |
57 | 58 | 70 |
71 |
72 | 73 |
74 |
75 |
76 | 77 | {% for project in projects %} 78 |
79 |
80 | 81 | project thumbnail 82 |
83 |

{{ project.title }}

84 |

By {{ project.user.first_name }} {{ project.user.last_name }}

85 |
{{ project.feedbackCount }}
86 |
87 | {% for tag in project.tags %} 88 | 89 | {{ tag }} 90 | 91 | {% endfor %} 92 |
93 |
94 | 95 |
96 |
97 | {% endfor %} 98 | 99 |
100 |
101 |
102 | 103 | 126 |
127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /templates/profile.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | DevSearch - Connect with Developers! 20 | 21 | 22 | 23 | 24 |
25 |
26 | 29 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 |
55 |
56 |
57 | 58 |

{{ user.first_name }} {{ user.last_name }}

59 |

{{ user.account.summary }}

60 |

{{ user.account.location }}

61 | 68 | Send Message 69 |
70 |
71 |
72 |
73 |
74 |

About Me

75 |

{{ user.account.about }}

76 |
77 |
78 |

Skills

79 |
80 | {% for skill in user.account.skill_set.all %} 81 |
82 |

{{ skill.name }}

83 |

{{ skill.description }}

84 |
85 | {% endfor %} 86 | 87 |

Other Skills

88 |
89 | {% for skill in user.account.other_skills %} 90 | 91 | {{ skill }} 92 | 93 | {% endfor %} 94 |
95 |
96 |
97 |
98 |

Projects

99 |
100 | 101 | {% for project in projects %} 102 |
103 |
104 | 105 | project thumbnail 106 |
107 |

{{ project.title }}

108 |

By {{ project.user.first_name }} {{ project.user.last_name }}

109 |
{{ project.feedbackCount }}
110 |
111 | {% for tag in project.tags %} 112 | 113 | {{ tag }} 114 | 115 | {% endfor %} 116 |
117 |
118 | 119 |
120 |
121 | {% endfor %} 122 |
123 |
124 |
125 |
126 |
127 |
128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /templates/account.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | DevSearch - Connect with Developers! 21 | 22 | 23 | 24 | 25 |
26 |
27 | 30 | 48 |
49 |
50 | 51 | 52 |
53 |
54 |
55 |
56 |
57 |
58 | Edit 59 | 60 |

{{ user.first_name }} {{ user.last_name }}

61 |

{{ user.account.summary }}

62 |

{{ user.account.location }}

63 | 70 |
71 |
72 |
73 |
74 |
75 |

About Me

76 |

77 | {{ user.account.about }} 78 |

79 |
80 |
81 |

Skills

82 | Add Skill 83 |
84 | 85 | 86 | {% for skill in user.account.skill_set.all %} 87 | 88 | 94 | 99 | {% endfor %} 100 |
89 |

{{ skill.name }}

90 |

91 | {{ skill.description }} 92 |

93 |
95 | Edit 96 | 97 | Delete 98 |
101 | 102 |
103 |

Projects

104 | Add Project 105 |
106 | 107 | 108 | {% for project in projects %} 109 | 110 | 113 | 119 | 124 | 125 | {% endfor %} 126 |
111 | Project Thumbnail 112 | 114 | {{ project.title }} 115 |

116 | {{ project.description }} 117 |

118 |
120 | Edit 121 | 122 | Delete 123 |
127 |
128 |
129 |
130 |
131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /static/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /users/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.contrib import messages 3 | 4 | from django.contrib.auth.models import User 5 | 6 | from django.contrib.auth.hashers import make_password 7 | from django.contrib.auth import authenticate, login, logout 8 | from django.contrib.auth.decorators import login_required 9 | 10 | from django.db import IntegrityError 11 | from django.http import HttpResponseForbidden 12 | 13 | from .models import Skill 14 | from main.models import Project 15 | 16 | 17 | def signup(request): 18 | if request.method == 'POST': 19 | if ' ' not in request.POST.get('name'): 20 | messages.info(request, 'Full name is incorrect!') 21 | elif request.POST.get('password') != request.POST.get('confirm-password'): 22 | messages.info(request, 'Passwords do not match!') 23 | 24 | else: 25 | user = User( 26 | first_name=request.POST.get('name').rsplit(' ')[0], 27 | last_name=request.POST.get('name').rsplit(' ')[1], 28 | username=request.POST.get('email'), 29 | email=request.POST.get('email'), 30 | password=make_password(request.POST.get('password')) 31 | ) 32 | try: 33 | user.save() 34 | return redirect('login') 35 | except IntegrityError: 36 | messages.info(request, 'This user is already exist!') 37 | 38 | return render(request, 'signup.html') 39 | 40 | 41 | def signin(request): 42 | if request.method == 'POST': 43 | user = authenticate( 44 | username=request.POST.get('email'), 45 | password=request.POST.get('password') 46 | ) 47 | if user is not None: 48 | login(request, user) 49 | return redirect('index') 50 | else: 51 | messages.info(request, 'Email or password is incorrect!') 52 | 53 | return render(request, 'login.html') 54 | 55 | 56 | def logOut(request): 57 | logout(request) 58 | return redirect('login') 59 | 60 | 61 | @login_required(login_url='login') 62 | def account(request): 63 | context = { 64 | 'user': request.user, 65 | 'projects': Project.objects.filter(user=request.user).order_by('-date') 66 | } 67 | return render(request, 'account.html', context) 68 | 69 | 70 | def profile(request, username): 71 | user = User.objects.get(username=username) 72 | context = { 73 | 'user': user, 74 | 'projects': Project.objects.filter(user=user).order_by('-date') 75 | } 76 | return render(request, 'profile.html', context) 77 | 78 | 79 | # ACCOUNT STUFF 80 | @login_required(login_url='login') 81 | def editAccount(request): 82 | user = request.user 83 | if request.method == 'POST': 84 | if ' ' not in request.POST.get('full_name'): 85 | messages.info(request, 'Full name is incorrect!') 86 | 87 | else: 88 | user.first_name = request.POST.get('full_name').rsplit(' ')[0] 89 | user.last_name = request.POST.get('full_name').rsplit(' ')[1] 90 | user.email = request.POST.get('email') 91 | user.account.summary = request.POST.get('summary') 92 | user.account.location = request.POST.get('location') 93 | user.account.about = request.POST.get('about') 94 | user.account.other_skills = request.POST.get('skills').rsplit(', ') 95 | if request.FILES.get('avatar') is not None: 96 | user.account.avatar = request.FILES.get('avatar') 97 | 98 | user.save() 99 | return redirect('account') 100 | 101 | return render(request, 'account-edit-form.html', {'user': user}) 102 | 103 | 104 | @login_required(login_url='login') 105 | def addSkill(request): 106 | if request.method == 'POST': 107 | skill = Skill( 108 | account=request.user.account, 109 | name=request.POST.get('name'), 110 | description=request.POST.get('desc') 111 | ) 112 | skill.save() 113 | return redirect('account') 114 | 115 | return render(request, 'skill-add-edit-form.html') 116 | 117 | 118 | @login_required(login_url='login') 119 | def editSkill(request, id): 120 | skill = Skill.objects.get(id=id) 121 | if skill.account.user == request.user: 122 | if request.method == 'POST': 123 | skill.name = request.POST.get('name') 124 | skill.description = request.POST.get('desc') 125 | skill.save() 126 | return redirect('account') 127 | else: 128 | return render(request, 'skill-add-edit-form.html', {'skill': skill}) 129 | else: 130 | return HttpResponseForbidden() 131 | 132 | 133 | @login_required(login_url='login') 134 | def deleteSkill(request, id): 135 | skill = Skill.objects.get(id=id) 136 | if skill.account.user == request.user: 137 | if request.method == 'POST': 138 | skill.delete() 139 | return redirect('account') 140 | else: 141 | context = { 142 | 'warning': f'Are your sure you want to delete this {skill.name} skill?' 143 | } 144 | return render(request, 'delete.html', context) 145 | else: 146 | return HttpResponseForbidden() 147 | 148 | 149 | @login_required(login_url='login') 150 | def addProject(request): 151 | if request.method == 'POST': 152 | project = Project( 153 | user=request.user, 154 | title=request.POST.get('title'), 155 | description=request.POST.get('description'), 156 | tags=request.POST.get('tags').rsplit(', '), 157 | link=request.POST.get('link'), 158 | image=request.FILES.get('image') 159 | ) 160 | project.save() 161 | return redirect('account') 162 | 163 | return render(request, 'project-add-edit-form.html') 164 | 165 | 166 | @login_required(login_url='login') 167 | def editProject(request, id): 168 | project = Project.objects.get(id=id) 169 | if project.user == request.user: 170 | if request.method == 'POST': 171 | project.title = request.POST.get('title') 172 | project.description = request.POST.get('description') 173 | project.link = request.POST.get('link') 174 | project.tags = request.POST.get('tags').rsplit(', ') 175 | if request.FILES.get('image'): 176 | project.image = request.FILES.get('image') 177 | 178 | project.save() 179 | return redirect('account') 180 | 181 | return render(request, 'project-add-edit-form.html', {'project': project}) 182 | 183 | else: 184 | return HttpResponseForbidden() 185 | 186 | 187 | @login_required(login_url='login') 188 | def deleteProject(request, id): 189 | project = Project.objects.get(id=id) 190 | if project.user == request.user: 191 | if request.method == 'POST': 192 | project.delete() 193 | return redirect('account') 194 | 195 | context = { 196 | 'warning': f'Are your sure you want to delete this {project.title} project?' 197 | } 198 | return render(request, 'delete.html', context) 199 | 200 | else: 201 | return HttpResponseForbidden() 202 | -------------------------------------------------------------------------------- /static/styles/app.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-sub: #2d2d39; 3 | } 4 | 5 | .btn--sub { 6 | background-color: var(--color-sub-light); 7 | } 8 | 9 | .card { 10 | border: 2px solid var(--color-light); 11 | } 12 | 13 | img { 14 | width: 100%; 15 | } 16 | 17 | .tag > i.im { 18 | font-size: 1.5rem; 19 | margin-right: 0.5rem; 20 | } 21 | 22 | .content-box { 23 | width: 95%; 24 | max-width: 85rem; 25 | margin: 0 auto; 26 | } 27 | 28 | /*======================= 29 | Header Section 30 | ========================*/ 31 | .header { 32 | position: sticky; 33 | z-index: 111; 34 | background-color: var(--color-sub); 35 | padding: 1.5rem 0; 36 | } 37 | 38 | .header > .container { 39 | display: flex; 40 | justify-content: space-between; 41 | align-items: center; 42 | } 43 | 44 | .header__logo > img { 45 | height: 3.8rem; 46 | } 47 | 48 | .header__nav > ul { 49 | display: inline-flex; 50 | align-items: center; 51 | list-style: none; 52 | gap: 6.5rem; 53 | } 54 | 55 | .header__menuItem > a { 56 | color: var(--color-light); 57 | font-weight: 300; 58 | } 59 | 60 | .toggle-menu > span { 61 | display: none; 62 | } 63 | 64 | .header__nav input[type='checkbox'] { 65 | display: none; 66 | } 67 | 68 | .header__nav input[type='checkbox']:checked ~ .header__menu { 69 | opacity: 1; 70 | pointer-events: all; 71 | } 72 | 73 | .project--thumbnail { 74 | height: 250px; 75 | object-fit: cover; 76 | } 77 | 78 | @media screen and (max-width: 800px) { 79 | .header__logo { 80 | position: relative; 81 | z-index: 999; 82 | } 83 | .toggle-menu { 84 | position: relative; 85 | } 86 | 87 | .toggle-menu > span { 88 | display: block; 89 | position: fixed; 90 | top: 1.5rem; 91 | right: 2.75rem; 92 | cursor: pointer; 93 | padding: 1rem; 94 | background: var(--color-sub-light); 95 | z-index: 999; 96 | font-size: 0; 97 | width: 5rem; 98 | height: 5rem; 99 | border-radius: 50%; 100 | } 101 | 102 | .toggle-menu__lines, 103 | .toggle-menu__lines::before, 104 | .toggle-menu__lines::after { 105 | pointer-events: none; 106 | content: ''; 107 | display: block; 108 | height: 2px; 109 | width: 2.5rem; 110 | position: fixed; 111 | top: 4rem; 112 | right: 4rem; 113 | z-index: 999; 114 | background: var(--color-light); 115 | transition: all 0.3s ease-in-out; 116 | } 117 | 118 | .toggle-menu__lines::before { 119 | top: 3.4rem; 120 | transform-origin: 0 0; 121 | } 122 | .toggle-menu__lines::after { 123 | top: 4.6rem; 124 | transform-origin: 0 0; 125 | } 126 | 127 | .header__nav input[type='checkbox']:checked + .toggle-menu > .toggle-menu__lines::before { 128 | margin-left: 3px; 129 | transform: rotate(45deg) scaleX(1.2) translateX(-2px) translateY(-4px); 130 | visibility: visible; 131 | } 132 | 133 | .header__nav input[type='checkbox']:checked + .toggle-menu > .toggle-menu__lines { 134 | visibility: hidden; 135 | } 136 | 137 | .header__nav input[type='checkbox']:checked + .toggle-menu > .toggle-menu__lines::after { 138 | transform: rotate(-45deg) scaleX(1.2) translateX(-2px) translateY(3px); 139 | visibility: visible; 140 | } 141 | 142 | .header__nav > ul { 143 | opacity: 0; 144 | pointer-events: none; 145 | flex-direction: column; 146 | justify-content: center; 147 | position: fixed; 148 | top: 0; 149 | left: 0; 150 | bottom: 0; 151 | right: 0; 152 | background: var(--color-sub); 153 | transition: all 0.5s ease-in-out; 154 | } 155 | 156 | .header__menuItem a { 157 | transition: all 0.5s ease-in-out; 158 | padding: 1rem 3rem; 159 | } 160 | 161 | .header__menuItem a:hover { 162 | padding: 1rem 3rem; 163 | border-radius: 3px; 164 | background: var(--color-sub-light); 165 | } 166 | } 167 | 168 | /*======================= 169 | Hero Section 170 | ========================*/ 171 | 172 | .hero-section { 173 | height: 40vh; 174 | display: flex; 175 | align-items: center; 176 | background-color: var(--color-sub); 177 | } 178 | 179 | .hero-section__box { 180 | margin-bottom: 4rem; 181 | } 182 | 183 | .hero-section__box > h2, 184 | .hero-section__box > h2 > span { 185 | font-size: 4rem; 186 | color: var(--color-light); 187 | line-height: 1.3; 188 | text-transform: uppercase; 189 | } 190 | 191 | @media screen and (max-width: 1000px) { 192 | .hero-section__box > h2, 193 | .hero-section__box > h2 > span { 194 | font-size: 3.4rem; 195 | } 196 | } 197 | 198 | @media screen and (max-width: 800px) { 199 | .hero-section__box > h2, 200 | .hero-section__box > h2 > span { 201 | font-size: 3rem; 202 | line-height: 1.5; 203 | } 204 | } 205 | 206 | @media screen and (max-width: 500px) { 207 | .hero-section__box > h2, 208 | .hero-section__box > h2 > span { 209 | font-size: 2.4rem; 210 | line-height: 1.5; 211 | } 212 | } 213 | 214 | @media screen and (max-width: 400px) { 215 | .hero-section__box > h2, 216 | .hero-section__box > h2 > span { 217 | font-size: 2rem; 218 | } 219 | } 220 | 221 | .hero-section__box > h2 { 222 | font-weight: 300; 223 | } 224 | 225 | .hero-section__box > h2 > span { 226 | font-weight: 700; 227 | } 228 | 229 | .hero-section__search > .form { 230 | display: flex; 231 | align-items: center; 232 | justify-content: center; 233 | gap: 2rem; 234 | } 235 | 236 | .hero-section__search > .form label { 237 | position: absolute; 238 | left: -9999px; 239 | } 240 | 241 | .hero-section__search > .form input.input--text { 242 | width: 50rem; 243 | } 244 | 245 | @media screen and (max-width: 700px) { 246 | .hero-section__search > .form { 247 | display: flex; 248 | flex-direction: column; 249 | gap: 0; 250 | align-items: stretch; 251 | } 252 | 253 | .hero-section__search > .form input.input--text, 254 | .hero-section__search > .form input { 255 | min-width: 100%; 256 | width: 100%; 257 | } 258 | } 259 | 260 | /*======================= 261 | Developers 262 | ========================*/ 263 | 264 | .devlist { 265 | margin-top: 8rem; 266 | } 267 | 268 | .devlist .grid { 269 | align-items: stretch; 270 | } 271 | 272 | .devlist .dev .dev__profile { 273 | display: flex; 274 | align-items: center; 275 | gap: 1.5rem; 276 | } 277 | 278 | .devlist .dev .dev__meta h3 { 279 | color: var(--color-sub); 280 | font-size: 2rem; 281 | font-weight: 700; 282 | } 283 | 284 | .devlist .dev .dev__meta h5 { 285 | color: var(--color-text); 286 | font-size: 1.4rem; 287 | font-weight: 500; 288 | } 289 | 290 | .devlist .dev .dev__info { 291 | margin: 1.5rem 0; 292 | font-size: 1.35rem; 293 | line-height: 1.4; 294 | text-align: justify; 295 | } 296 | 297 | /*======================= 298 | Pagination 299 | ========================*/ 300 | .pagination { 301 | margin-top: 8rem; 302 | margin-bottom: 10rem; 303 | } 304 | 305 | .pagination > ul { 306 | flex-wrap: wrap; 307 | list-style: none; 308 | display: flex; 309 | align-items: center; 310 | justify-content: center; 311 | gap: 1rem; 312 | } 313 | 314 | .pagination .btn { 315 | color: var(--color-sub); 316 | } 317 | 318 | .pagination .btn.btn--sub { 319 | color: var(--color-light); 320 | } 321 | 322 | /*======================= 323 | Profile 324 | ========================*/ 325 | 326 | .profile .card > .dev { 327 | padding: 4rem; 328 | } 329 | 330 | .dev__avatar { 331 | display: block; 332 | margin: auto; 333 | } 334 | 335 | .dev__name { 336 | color: var(--color-sub); 337 | font-size: 2.8rem; 338 | font-weight: 700; 339 | margin-top: 1rem; 340 | margin-bottom: 0.5rem; 341 | } 342 | 343 | .dev__title { 344 | font-size: 1.6rem; 345 | font-weight: 500; 346 | line-height: 1.3; 347 | } 348 | 349 | .dev__location { 350 | margin-top: 1rem; 351 | } 352 | 353 | .dev__social { 354 | margin: 3rem 0; 355 | list-style: none; 356 | display: flex; 357 | justify-content: center; 358 | gap: 1.5rem; 359 | } 360 | 361 | .devInfo { 362 | margin-bottom: 3rem; 363 | } 364 | 365 | .devInfo__title, 366 | .devInfo__subtitle { 367 | font-size: 2.6rem; 368 | color: var(--color-sub); 369 | text-transform: uppercase; 370 | font-weight: 700; 371 | margin-bottom: 1.5rem; 372 | } 373 | 374 | .devInfo__subtitle { 375 | font-size: 2rem; 376 | color: var(--color-sub-light); 377 | } 378 | 379 | .devInfo__about, 380 | .devSkill__info { 381 | font-size: 1.45rem; 382 | line-height: 1.7; 383 | text-align: justify; 384 | } 385 | 386 | .devSkill { 387 | margin-bottom: 3rem; 388 | } 389 | 390 | .devSkill__title { 391 | font-size: 1.65rem; 392 | font-weight: 500; 393 | color: var(--color-sub-light); 394 | } 395 | 396 | @media screen and (min-width: 700px) { 397 | .devSkill { 398 | display: flex; 399 | justify-content: space-between; 400 | gap: 3rem; 401 | } 402 | 403 | .devSkill__info { 404 | flex-basis: 80%; 405 | } 406 | } 407 | 408 | /*======================= 409 | Single Projects 410 | ========================*/ 411 | 412 | .singleProject .column--1of3 { 413 | order: 2; 414 | } 415 | 416 | @media screen and (min-width: 1081px) { 417 | .singleProject .column--1of3 { 418 | order: 1; 419 | } 420 | .singleProject .column--2of3 { 421 | order: 2; 422 | } 423 | } 424 | 425 | .singleProject__toolStack { 426 | margin-top: 2rem; 427 | margin-bottom: 4rem; 428 | } 429 | 430 | .singleProject__liveLink { 431 | font-weight: 500; 432 | transition: all 0.3s ease-in-out; 433 | border-bottom: 2px solid transparent; 434 | } 435 | 436 | .singleProject__liveLink:hover { 437 | font-weight: 500; 438 | border-bottom: 2px solid var(--color-main); 439 | } 440 | 441 | .singleProject__liveLink i { 442 | display: inline-block; 443 | font-size: 1.6rem; 444 | margin-right: 1rem; 445 | } 446 | 447 | .singleProject__preview { 448 | height: 50vh; 449 | object-fit: cover; 450 | object-position: top center; 451 | margin-bottom: 3rem; 452 | border-radius: 0.7rem; 453 | } 454 | 455 | .singleProject__developer { 456 | font-size: 1.8rem; 457 | font-weight: 500; 458 | } 459 | 460 | .singleProject__title { 461 | font-size: 3.6rem; 462 | font-weight: 700; 463 | color: var(--color-sub); 464 | margin-bottom: 3rem; 465 | } 466 | 467 | .project__author{ 468 | font-size: 1.4rem; 469 | font-style: italic; 470 | } 471 | 472 | .project--rating{ 473 | margin-bottom: 1.6em; 474 | margin-top: .5em; 475 | } 476 | 477 | .singleProject__subtitle { 478 | text-transform: uppercase; 479 | font-size: 2.4rem; 480 | font-weight: 700; 481 | color: var(--color-sub-light); 482 | margin-bottom: 1rem; 483 | } 484 | 485 | /*======================= 486 | Projects Page 487 | ========================*/ 488 | .projects > .hero-section { 489 | background: var(--color-sub-lighter); 490 | height: 30vh; 491 | } 492 | 493 | .projects .hero-section__box { 494 | margin-bottom: 2.5rem; 495 | } 496 | 497 | .projects .hero-section__box > h2 { 498 | color: var(--color-sub); 499 | } 500 | 501 | .projects .hero-section__box > h2 > span { 502 | font-weight: 700; 503 | color: var(--color-sub); 504 | } 505 | 506 | .projectsList { 507 | margin-top: 8rem; 508 | } 509 | 510 | /*========== Projects ==========*/ 511 | 512 | .project__thumbnail { 513 | height: 25rem; 514 | object-fit: cover; 515 | object-position: top center; 516 | } 517 | 518 | .project__title { 519 | font-size: 2rem; 520 | font-weight: 700; 521 | color: var(--color-sub); 522 | } 523 | 524 | .project__info { 525 | margin: 1rem 0; 526 | font-size: 1.4rem; 527 | text-align: justify; 528 | } 529 | 530 | /*========== Comments ==========*/ 531 | 532 | .comments { 533 | margin-top: 4rem; 534 | padding-top: 3rem; 535 | border-top: 2px solid var(--color-light); 536 | } 537 | 538 | .comments .form label { 539 | position: absolute; 540 | margin: -9999px; 541 | } 542 | 543 | .commentList { 544 | margin: 3rem 0; 545 | } 546 | 547 | .comment { 548 | display: flex; 549 | gap: 2rem; 550 | } 551 | 552 | .comment:not(:last-child) { 553 | margin-bottom: 3rem; 554 | } 555 | 556 | .comment__author { 557 | font-size: 1.8rem; 558 | } 559 | 560 | .comment__info { 561 | font-size: 1.45rem; 562 | } 563 | 564 | /*======================= 565 | Account Settings 566 | ========================*/ 567 | 568 | .settingsPage .dev > .settings__btn { 569 | display: inline-block; 570 | margin-bottom: 3rem; 571 | } 572 | 573 | .settings { 574 | display: flex; 575 | justify-content: space-between; 576 | align-items: center; 577 | } 578 | 579 | .settings__title { 580 | font-size: 2.4rem; 581 | text-transform: uppercase; 582 | font-weight: 700; 583 | color: var(--color-sub); 584 | } 585 | 586 | .settings__table { 587 | margin-top: 3rem; 588 | margin-bottom: 5rem; 589 | padding: 0 3rem; 590 | background: var(--color-white); 591 | } 592 | 593 | .settings__table tr { 594 | width: 100%; 595 | } 596 | 597 | .settings__table tr > td { 598 | padding: 2rem 0; 599 | } 600 | 601 | .settings__table tr:not(:last-child) td { 602 | border-bottom: 2px solid var(--color-white-light); 603 | } 604 | 605 | .settings__thumbnail { 606 | width: 23%; 607 | min-width: 10rem; 608 | } 609 | 610 | .settings__thumbnail img { 611 | padding-right: 3rem; 612 | height: 15rem; 613 | object-fit: contain; 614 | } 615 | 616 | .settings__tableInfo{ 617 | width: 100%; 618 | } 619 | 620 | .settings__tableInfo h4, 621 | .settings__tableInfo a { 622 | padding-right: 1rem; 623 | width: auto; 624 | font-size: 1.6rem; 625 | font-weight: 500; 626 | color: var(--color-sub); 627 | } 628 | 629 | .settings__tableInfo p { 630 | font-size: 1.4rem; 631 | } 632 | 633 | .settings__tableActions { 634 | text-align: right; 635 | } 636 | 637 | /*======================= 638 | Login/SignUp 639 | ========================*/ 640 | 641 | .auth { 642 | width: 100%; 643 | position: relative; 644 | z-index: 1; 645 | background: transparent; 646 | height: 99vh; 647 | display: flex; 648 | justify-content: center; 649 | align-items: center; 650 | } 651 | 652 | .auth::after { 653 | z-index: -1; 654 | opacity: 0.1; 655 | content: ''; 656 | display: block; 657 | position: fixed; 658 | top: 0; 659 | left: 0; 660 | bottom: 0; 661 | right: 0; 662 | background: url(../images/pattern.jpg); 663 | } 664 | 665 | .auth > .card { 666 | padding: 3rem 4rem; 667 | width: 95%; 668 | max-width: 65rem; 669 | } 670 | 671 | @media screen and (min-width: 650px) { 672 | .auth > .card { 673 | padding: 5rem 8rem; 674 | width: 95%; 675 | max-width: 65rem; 676 | } 677 | } 678 | 679 | .auth__header h3 { 680 | color: var(--color-sub); 681 | font-weight: 700; 682 | margin-top: 1rem; 683 | } 684 | 685 | .auth__header p { 686 | font-weight: 400; 687 | font-size: 1.8rem; 688 | margin-bottom: 2rem; 689 | } 690 | 691 | .auth__form { 692 | margin: 3rem 0; 693 | } 694 | 695 | .auth .auth__form label, 696 | .auth .auth__alternative p { 697 | font-size: 1.4rem; 698 | font-weight: 500; 699 | color: var(--color-sub-light); 700 | } 701 | 702 | .auth .auth__actions { 703 | display: flex; 704 | justify-content: space-between; 705 | align-items: center; 706 | margin: 2.5rem 0; 707 | } 708 | .auth .auth__actions a { 709 | text-decoration: underline; 710 | } 711 | 712 | .auth .auth__alternative a { 713 | font-size: 2rem; 714 | font-weight: 500; 715 | } 716 | 717 | /*======================= 718 | Inbox 719 | ========================*/ 720 | 721 | .inbox { 722 | height: 90vh; 723 | } 724 | 725 | .inbox__title, 726 | .inbox__title span { 727 | font-size: 2.4rem; 728 | font-weight: 700; 729 | color: var(--color-sub); 730 | text-transform: uppercase; 731 | text-align: left; 732 | margin-bottom: 3rem; 733 | } 734 | 735 | .inbox__title span { 736 | color: var(--color-main); 737 | } 738 | 739 | .messages { 740 | list-style: none; 741 | background: var(--color-white); 742 | padding: 0.5rem 3rem; 743 | border: 2px solid var(--color-light); 744 | border-radius: 0.7rem; 745 | } 746 | 747 | .message { 748 | padding: 2.5rem 0; 749 | } 750 | 751 | .message > a span { 752 | font-size: 1.45rem; 753 | font-weight: 500; 754 | } 755 | 756 | .message.message--unread a span { 757 | font-weight: 700; 758 | color: var(--color-sub); 759 | } 760 | 761 | .message:not(:last-child) { 762 | border-bottom: 2px solid var(--color-white-light); 763 | } 764 | 765 | .message > a { 766 | display: flex; 767 | gap: 1rem; 768 | } 769 | 770 | .message__author, 771 | .message__date { 772 | flex-basis: 25%; 773 | } 774 | 775 | .message__subject { 776 | flex-basis: 50%; 777 | } 778 | 779 | .message__date { 780 | text-align: right; 781 | } 782 | 783 | /*======================= 784 | Single Message 785 | ========================*/ 786 | 787 | .messagePage .message { 788 | list-style: none; 789 | background: var(--color-white); 790 | padding: 3.5rem 5rem; 791 | border: 2px solid var(--color-light); 792 | border-radius: 0.7rem; 793 | } 794 | 795 | .messagePage .message__subject { 796 | font-size: 2.8rem; 797 | color: var(--color-sub); 798 | margin-bottom: 1rem; 799 | margin-top: 2rem; 800 | } 801 | 802 | .messagePage .message__author { 803 | font-size: 1.8rem; 804 | font-weight: 500; 805 | } 806 | 807 | .messagePage .message__date { 808 | font-size: 1.4rem; 809 | font-weight: 400; 810 | text-align: left; 811 | margin-bottom: 3rem; 812 | } 813 | 814 | .backButton { 815 | background: var(--color-main-light); 816 | color: var(--color-main); 817 | width: 4rem; 818 | height: 4rem; 819 | display: flex; 820 | align-items: center; 821 | justify-content: center; 822 | border-radius: 50%; 823 | } 824 | 825 | .backButton i { 826 | font-size: 1.8rem; 827 | } 828 | 829 | 830 | /*======================= 831 | Form Page 832 | ========================*/ 833 | 834 | .formPage .formWrapper { 835 | list-style: none; 836 | background: var(--color-white); 837 | padding: 3.5rem 5rem; 838 | border: 2px solid var(--color-light); 839 | border-radius: 0.7rem; 840 | } 841 | 842 | .formPage .formWrapper__subject { 843 | font-size: 2.8rem; 844 | color: var(--color-sub); 845 | margin-bottom: 1rem; 846 | margin-top: 2rem; 847 | } 848 | -------------------------------------------------------------------------------- /static/uikit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 🎨 Mumble UI 23 | 24 | 25 | 26 |
27 |
28 |

🎨 Mumble UI

29 |

30 | This UI Kit was primarily designed for mumble.dev for creating re-usable 31 | components. You can copy styles folder and link the mumble ui css by adding 32 |

<link rel="stylesheet" href="styles/index.css" />
onto the 33 |
<head>
tag. 34 |

35 |
36 |
37 |
38 | 39 |
40 |

Font Sizes

41 |

42 | Preset settings for default font sizes. Sizes are set in pixels at the moment but will be modified to use rem. 43 | Default font family is: "Poppins" and for the monospace font it's "Fira Code". 44 |

45 | 46 |
47 |

Heading H1 (48px)

48 |

Heading H2 (36px)

49 |

Heading H3 (32px)

50 |

Heading H4 (28px)

51 |
Heading H5 (24px)
52 |
Heading H6 (18px)
53 |

Paragraph text (16px)

54 | 55 | Span Tag (16px) 56 |
57 | Strong tag (16px) 58 |
59 | 60 |
 61 |           
 62 |             <h1>Heading H1 (48px)</h1>
 63 |             <h2>Heading H2 (36px)</h2>
 64 |             <h3>Heading H3 (32px)</h3>
 65 |             <h4>Heading H4 (28px)</h4>
 66 |             <h5>Heading H5 (24px)</h5>
 67 |             <h6>Heading H6 (18px)</h6>
 68 |             <p>Paragraph text (16px)</p>
 69 |             <span>Span Tag (16px)</span>
 70 |             <strong>Strong tag (16px)</strong>
 71 |           
 72 |         
73 |
74 | 75 | 76 |
77 |

Buttons

78 |

79 | Choose between two styles, three variants and three sizes buttons by using the "btn" class with additional 80 | settings. "btn" should be using with all buttons followed by the button style (Filled or outlined). Setting a 81 | size is optional. 82 |

83 |
84 | 85 | 86 | 87 |

88 | 89 | 90 |

91 | 92 | 93 |
94 |
 95 |           
 96 |             <button class="btn btn--main">Button Main</button>
 97 |             <button class="btn btn--sub">Button Sub</button>
 98 |             <button class="btn">Button Default</button>
 99 | 
100 |             <button class="btn btn--main--outline">Button Main Outline</button>
101 |             <button class="btn btn--sub--outline">Button Sub Outline</button>
102 | 
103 |             <button class="btn btn--main btn--sm">Button Small</button>
104 |             <button class="btn btn--sub btn--lg">Button Large</button>
105 |           
106 |         
107 |
108 | 109 | 110 |
111 |

Tag

112 |

113 | To use the tag styling use the "<small>" tag inside of a div and give it the class of "tag." 114 |

115 | 116 |
117 | 118 | This is a tag 119 | 120 | 121 | This is a tag 122 | 123 |
124 | 125 |
126 |           
127 |             <span class="tag"> 
128 |               <small>This is a tag</small>
129 |             </span> 
130 |             
131 |             <span class="tag tag--outline"> 
132 |               <small>This is a tag</small> 
133 |             </span>
134 |           
135 |         
136 |
137 | 138 | 139 |
140 |

Card

141 |

142 | Choose between 2 card styles. Each card showed contain the class of "card". By default the "card" class will 143 | give you a light themed card, to choose style to add a second class "card-dark" 144 |

145 |
146 |
147 |
148 |

Card Title

149 |

Some quick example text to build on the card title and make up the bulk of the card's content.

150 |
151 |
152 | 153 |
154 |
155 |

Card Dark Title

156 |

Some quick example text to build on the card title and make up the bulk of the card's content.

157 |
158 |
159 |
160 | 161 |
162 |           
163 |             <div class="card" style="width: 320px">
164 |               <div class="card__body">
165 |                 <h3>Card Title</h3>
166 |                 <p>
167 |                   Some quick example text to build on the card title and make up
168 |                   the bulk of the card's content.
169 |                 </p>
170 |               </div>
171 |             </div>
172 | 
173 |             <div class="card card--dark" style="width: 320px">
174 |               <div class="card__body">
175 |                 <h3>Card Dark Title</h3>
176 |                 <p>
177 |                   Some quick example text to build on the card title and make up
178 |                   the bulk of the card's content.
179 |                 </p>
180 |               </div>
181 |             </div>
182 |           
183 |         
184 |
185 | 186 | 187 |
188 |

User Avatar

189 |

Choose between 4 sizes for a user thumbnail icon.

190 |
191 | 192 | 193 | 194 | 195 |
196 | 197 |
198 |             
199 |               <img class="avatar avatar--xl" src="#" />
200 |               <img class="avatar avatar--lg" src="#" />
201 |               <img class="avatar avatar--md" src="#" />
202 |               <img class="avatar avatar--sm" src="#" />
203 |             
204 |         
205 |
206 | 207 | 208 |
209 |

Loading

210 |

Page loading component which takes up entire viewport.

211 |
212 |
213 |
214 | 215 | 216 | 217 | 218 | 219 |
220 |
221 |
222 | 223 |
224 |         
225 |           <div class="loading">
226 |             <div class="loading__loader">
227 |               <span></span>
228 |               <span></span>
229 |               <span></span>
230 |               <span></span>
231 |               <span></span>
232 |             </div>
233 |           </div> 
234 |         
235 |       
236 |
237 | 238 | 239 |
240 |

Alert

241 |

Show Alert message with the alert components. There are three variants of alert box.

242 |
243 |
244 |

This is an error message!

245 | 246 |
247 |
248 |

This is an error message!

249 | 250 |
251 |
252 |

This is an error message!

253 | 254 |
255 |
256 | 257 |
258 |         
259 |           <div class="alert">
260 |             <p class="alert__message">This is an error message!</p>
261 |             <button class="alert__close">⨯</button>
262 |           </div>
263 | 
264 |           <div class="alert alert--success">
265 |             <p class="alert__message">This is an error message!</p>
266 |             <button class="alert__close">⨯</button>
267 |           </div>
268 | 
269 |           <div class="alert alert--error">
270 |             <p class="alert__message">This is an error message!</p>
271 |             <button class="alert__close">⨯</button>
272 |           </div>
273 |         
274 |       
275 |
276 | 277 | 278 | 279 |
280 |

Grid and Layout

281 |

Mumble UI is based on 3 columns layout. The main difference between grid and layout is that the grid can grow and can be used for listing items where the layout is fixed to certain number of children. By default the grid and layout comes with mobile responsiveness.

282 |
283 |
284 |
Grid
285 |
286 |
1
287 |
2
288 |
3
289 |
4
290 |
5
291 |
292 | 293 |
294 |
1
295 |
2
296 |
3
297 |
4
298 |
5
299 |
300 | 301 |
Layout
302 |
303 |
1 0f 2
304 |
1 of 2
305 |
306 | 307 |
308 |
1 of 3
309 |
1 of 3
310 |
1 of 3
311 |
312 | 313 |
314 |
1 of 3
315 |
3 of 1
316 |
317 |
318 |
319 | 320 |
321 |         
322 |           
323 | <!-- Grid 3 Columns --> 324 | <div class="grid grid--three"> 325 | <div class="column">1</div> 326 | <div class="column">2</div> 327 | <div class="column">3</div> 328 | <div class="column">4</div> 329 | <div class="column">5</div> 330 | </div> 331 | 332 | <!-- Grid 2 Columns --> 333 | <div class="grid grid--two"> 334 | <div class="column">1</div> 335 | <div class="column">2</div> 336 | <div class="column">3</div> 337 | <div class="column">4</div> 338 | <div class="column">5</div> 339 | </div> 340 | 341 | <!-- 2 Columns Layout --> 342 | <div class="layout"> 343 | <div class="column column--1of2">1 of 2</div> 344 | <div class="column column--1of2">1 of 2</div> 345 | </div> 346 | 347 | <!-- 3 Columns Layout --> 348 | <div class="layout"> 349 | <div class="column column--1of3">1 of 3</div> 350 | <div class="column column--1of3">1 of 3</div> 351 | <div class="column column--1of3">1 of 3</div> 352 | </div> 353 | 354 | <!-- 3 Columns Uneven Layout --> 355 | <div class="layout"> 356 | <div class="column column--1of3">1 of 3</div> 357 | <div class="column column--2of3">2 of 3</div> 358 | </div> 359 |
360 |
361 |
362 |
363 | 364 | 365 |
366 |

Form and Inputs

367 |

368 | Introducing the basic form and input fields. By default the form field with take the entire space of a 369 | container. Make sure your are using the class name properly. More form fields are coming soon... 🏃‍♂️. Feel 370 | free 371 | to contributes following the naming convention. 372 |

373 |
374 |
375 | 376 |
377 | 378 | 380 |
381 | 382 | 383 |
384 | 385 | 387 |
388 | 389 | 390 |
391 | 392 | 394 |
395 | 396 | 397 |
398 |

Favorite Color:

399 | 400 | 401 | 402 | 403 | 404 | 405 |
406 | 407 | 408 |
409 |

Your Hobby:

410 | 412 | 413 | 415 | 416 | 418 | 419 |
420 | 421 | 422 |
423 | 424 | 426 |
427 | 428 | 429 |
430 | 431 | 432 |
433 |
434 |
435 | 436 |
437 |             
438 |               <form action="#" class="form">
439 |                 <!-- Input:Text -->
440 |                 <div class="form__field">
441 |                   <label for="formInput#text">Full Name: </label>
442 |                   <input
443 |                     class="input input--text"
444 |                     id="formInput#text"
445 |                     type="text"
446 |                     name="text"
447 |                     placeholder="Enter your full name"
448 |                   />
449 |                 </div>
450 | 
451 |                 <!-- Input:Email -->
452 |                 <div class="form__field">
453 |                   <label for="formInput#email">Email: </label>
454 |                   <input
455 |                     class="input input--email"
456 |                     id="formInput#email"
457 |                     type="email"
458 |                     name="email"
459 |                     placeholder="e.g. user@domain.com"
460 |                   />
461 |                 </div>
462 | 
463 |                 <!-- Input:Password -->
464 |                 <div class="form__field">
465 |                   <label for="formInput#password">Password: </label>
466 |                   <input
467 |                     class="input input--password"
468 |                     id="formInput#passowrd"
469 |                     type="password"
470 |                     name="password"
471 |                     placeholder="••••••••"
472 |                   />
473 |                 </div>
474 | 
475 |                 <!-- Input:Radio -->
476 |                 <div class="form__field form__field--radio">
477 |                   <p>Favorite Color:</p>
478 |                   <input 
479 |                     class="input input--radio" 
480 |                     id="formInput#radio-01" 
481 |                     type="radio" 
482 |                     value="red" 
483 |                     name="color" 
484 |                   />
485 |                   <label for="formInput#radio-01">Red</label>
486 |                   <input 
487 |                     class="input input--radio" 
488 |                     id="formInput#radio-02" 
489 |                     type="radio" 
490 |                     value="green" 
491 |                     name="color" 
492 |                   />
493 |                   <label for="formInput#radio-02">Green</label>
494 |                   <input 
495 |                     class="input input--radio" 
496 |                     id="formInput#radio-03" 
497 |                     type="radio" 
498 |                     value="blue" 
499 |                     name="color" 
500 |                   />
501 |                   <label for="formInput#radio-03">Blue</label>
502 |                 </div>
503 | 
504 |                 <!-- Input:Checkbox -->
505 |                 <div class="form__field form__field--checkbox">
506 |                   <p>Your Hobby:</p>
507 |                   <input
508 |                     class="input input--checkbox"
509 |                     id="formInput#checkbox-01"
510 |                     type="checkbox"
511 |                     value="riding"
512 |                     name="hobby"
513 |                   />
514 |                   <label for="formInput#checkbox-01">Riding Bike</label>
515 |                   <input
516 |                     class="input input--checkbox"
517 |                     id="formInput#checkbox-02"
518 |                     type="checkbox"
519 |                     value="surfing"
520 |                     name="hobby"
521 |                   />
522 |                   <label for="formInput#checkbox-02">Surfing Sea</label>
523 |                   <input
524 |                     class="input input--checkbox"
525 |                     id="formInput#checkbox-03"
526 |                     type="checkbox"
527 |                     value="brewing-beer"
528 |                     name="hobby"
529 |                   />
530 |                   <label for="formInput#checkbox-03">Brewing Beer</label>
531 |                 </div>
532 | 
533 |                 <!-- Textarea -->
534 |                 <div class="form__field">
535 |                   <label for="formInput#textarea">Message: </label>
536 |                   <textarea
537 |                     class="input input--textarea"
538 |                     name="message"
539 |                     id="formInput#textarea"
540 |                     placeholder="Write something awesome..."
541 |                   ></textarea>
542 |                 </div>
543 | 
544 |                 <!-- Input:Submit / Input:Reset  -->
545 |                 <div class="form__field form__field--action">
546 |                   <input class="btn" type="reset" value="Reset" />
547 |                   <input class="btn btn--main" type="submit" value="Submit" />
548 |                 </div>
549 |               </form>
550 |             
551 |         
552 |
553 | 554 |

Thank you!

555 |
556 | 557 | 558 | 559 | 560 | 561 | 562 | --------------------------------------------------------------------------------