diff --git a/index.css b/index.css new file mode 100644 index 0000000..2a7c434 --- /dev/null +++ b/index.css @@ -0,0 +1,180 @@ +@import url('./web_flask/static/css/variables.css'); + +body { + margin: 0; + padding: 0; + font-family: var(--main-font); +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} + +.container { + width: 80%; + margin: 0 auto; +} + +header { + background-color: var(--primary-color); + color: var(--white); + padding: 1rem 0; +} + +header .container { + display: flex; + justify-content: flex-start; + align-items: center; + gap: 2rem; +} + +header .container .brand { + color: var(--black); + font-size: 1.2rem; + font-weight: 600; +} + +header .container .brand a { + color: var(--white); + text-decoration: none; +} + +header .container .brand span { + height: 300px; + width: 300px; + border-radius: 50%; + background-color: var(--white); + color: var(--primary-color); + margin-right: 0.2rem; + padding: 0.5rem 0.6rem; +} + + +nav ul { + list-style: none; + padding: 0; + margin: 0; +} + +nav ul li { + display: inline; + margin-right: 20px; +} + +nav a { + text-decoration: none; + color: #fff; + font-weight: bold; +} + +#hero { + background-image: url('./web_flask/static/image/hero\ image.jpg'); + background-size: cover; + background-position: center; + color: #fff; + text-align: center; + padding: 10rem 0; +} + +#hero .container { + background-color:rgba(0, 0, 0, 0.4); + padding: 2rem; + width: 50%; + border-radius: 2rem; +} + +#hero h2 { + font-size: 2.5rem; + margin-bottom: 20px; +} + +#hero p { + font-size: 1.2rem; + margin-bottom: 40px; +} + +.btn { + display: inline-block; + padding: 10px 20px; + background: none; + border: solid 1px var(--white); + color: #fff; + text-decoration: none; + border-radius: 5px; +} + +/* end of hero */ + +/* feature begins */ +#features { + background-color: var(--white); + width: 100%; + padding: 70px 0; + text-align: center; +} + +#features h2 { + font-size: 2.5rem; + margin: 40px auto; +} + +.feature { + display: flex; + justify-content: flex-start; + align-items: center; + width: 100%; + gap: 1rem; +} + +.feature-img img { + max-width: 100%; + height: auto; +} + +.feature-content { + padding: 0 20px; + width: 50%; + text-align: left; +} + +.feature h3 { + font-size: 1.5rem; + margin-bottom: 10px; +} + +.feature p { + font-size: 1rem; + color: #666; +} + +/* feature ends */ + +#about { + background-color: #f4f4f4; + padding: 70px 0; + text-align: center; +} + +#about h2 { + font-size: 2.5rem; + margin-bottom: 20px; +} + +#about .about-description, +#about .description-team { + text-align: left; +} + +#about .description-team .team { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; +} + +.team-member img { + width: 20%; + border-radius: 2rem; +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..0673593 --- /dev/null +++ b/index.html @@ -0,0 +1,124 @@ + + + + + + + + JobCircus - Landing Page + + + + + +
+
+

Welcome to JobCircus

+

Unlock Your Potential: Join Our Team and Transform Possibilities into Achievements!

+ Explore +
+
+ +
+
+

Features

+
+
+ + Feature 1 +
+
+

Job Management

+

Simplify your hiring process with our Job Listing Management feature. Effortlessly create, manage, and track job listings for streamlined recruitment. Elevate your hiring strategy and attract top talent with ease.

+
+
+ +
+
+

Streamlining Part-Time and Full-Time Job Searches

+

Refine your search with our Job Filter feature, tailored to part-time and full-time positions. Easily narrow down candidates, streamline your hiring process, and make confident decisions for every role.

+
+
+ + Feature 2 +
+
+ +
+
+ + Feature 3 +
+
+

Easy Job Search

+

Effortlessly find the perfect candidates with our Simple Search feature. Streamline your search process, discover top talent, and make hiring decisions with ease.

+
+
+
+
+ + +
+
+

About Us

+
+

+ At JobCircus, our journey began with a shared vision to revolutionize the job market, making connections between employers and job seekers seamless and efficient. Inspired by the belief that technology can transform traditional recruitment, we embarked on this project to create a platform that empowers individuals on both sides of the job market. +

+

This project serves as a portfolio endeavor for the ALX Software Engineering program, demonstrating our commitment to practical application of skills and innovation. By envisioning a scenario where employers and job seekers can effortlessly connect, we aim to showcase the power of technology in transforming traditional recruitment methods and empowering individuals on both sides of the job market.

+
+
+

Team JobCircus

+
+
+ Team Member 1 +

Kingsley Akpan

+ +
+
+ Team Member 2 +

Emmanuel Jonah

+ +
+
+ Team Member 3 +

Ruth Endurance

+ +
+
+
+
+

+ Connect with us on social media, and feel free to explore the codebase on our GitHub repository: +

+ + GitHub Repository +
+
+
+ + \ No newline at end of file diff --git a/jobcircus.YAML b/jobcircus.YAML deleted file mode 100644 index 813c565..0000000 --- a/jobcircus.YAML +++ /dev/null @@ -1,30 +0,0 @@ -name: Deploy Jobcircus - -on: - pull_request: - branches: - - master - -jobs: - deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - - name: Deploy to server - run: | - ssh ubuntu@100.25.158.180 'cd /home/ubuntu/jc/job_circus && git pull origin master' - echo "JobCircus for Life!!!" - diff --git a/web_flask/forms/application.py b/web_flask/forms/application.py index 45048ab..e93cc99 100644 --- a/web_flask/forms/application.py +++ b/web_flask/forms/application.py @@ -7,6 +7,6 @@ class ApplicationForm(FlaskForm): """Class extended from flask wtf for form login""" cover_letter = TextAreaField('Cover Letter', validators=[InputRequired(), - Length(min=50, max=268)], + Length(min=1, max=5000)], render_kw={'style': 'height: 10rem; resize: none;'}) submit = SubmitField('Submit') \ No newline at end of file diff --git a/web_flask/forms/login.py b/web_flask/forms/login.py index 7b08175..93ee9f1 100644 --- a/web_flask/forms/login.py +++ b/web_flask/forms/login.py @@ -8,7 +8,7 @@ class LoginForm(FlaskForm): """Class extended from flask wtf for form login""" email = StringField('Username', validators=[InputRequired(), - Length(min=4, max=100)]) + Length(min=1, max=100)]) password = PasswordField('Password', validators=[InputRequired(), - Length(min=4, max=100)]) + Length(min=1, max=100)]) submit = SubmitField('Login') \ No newline at end of file diff --git a/web_flask/forms/post_job.py b/web_flask/forms/post_job.py index 9e096a0..5b9a8be 100644 --- a/web_flask/forms/post_job.py +++ b/web_flask/forms/post_job.py @@ -7,16 +7,16 @@ class PostJobForm(FlaskForm): """Pst job form class""" title = StringField('Title', validators=[InputRequired(), - Length(min=4, max=30)]) + Length(min=1, max=30)]) description = TextAreaField('Description', validators=[InputRequired(), Length(min=10, max=2000)], render_kw={'style': 'height: 5rem; resize: none;'}) location = StringField('loation', validators=[InputRequired(), - Length(min=4, max=100)]) + Length(min=1, max=100)]) salary = StringField('Salary', validators=[InputRequired(), Length(min=1, max=15)]) requirements = StringField('Requirements', validators=[InputRequired(), - Length(min=4, max=1000)]) + Length(min=1, max=1000)]) type = RadioField('type', choices=['Partime', 'Fulltime'], validators=[InputRequired()]) deadline = DateField('Deadline', format='%Y-%m-%d') diff --git a/web_flask/forms/register.py b/web_flask/forms/register.py index 47e0e13..952b839 100644 --- a/web_flask/forms/register.py +++ b/web_flask/forms/register.py @@ -11,16 +11,16 @@ class RegisterForm(FlaskForm): lastname = StringField('Lastname', validators=[InputRequired(), Length(min=4, max=15)]) username = StringField('Username', validators=[InputRequired(), - Length(min=4, max=15)]) + Length(min=4, max=15)], render_kw={'autocomplete': 'username'}) email = StringField('Email', validators=[InputRequired(), - Length(min=4, max=100)]) + Length(min=4, max=100)], render_kw={'autocomplete': 'username'}) + portfolio_url = StringField('Portfolio Url', render_kw={'autocomplete': 'username'}) + github_url = StringField('Github Url', render_kw={'autocomplete': 'username'}) password = PasswordField('Password', validators=[InputRequired(), Length(min=4, max=100)]) confirm_password = PasswordField('Password', validators=[InputRequired(), Length(min=4, max=100), EqualTo('password')]) - portfolio_url = StringField('Portfolio Url') - github_url = StringField('Github Url') role = RadioField('role', choices=['Employer', 'Job-Seeker'], validators=[InputRequired()]) diff --git a/web_flask/models/application.py b/web_flask/models/application.py index e1a133a..97ba522 100644 --- a/web_flask/models/application.py +++ b/web_flask/models/application.py @@ -10,5 +10,5 @@ class Application(BaseModel, Base): __tablename__ = 'applications' user_id = Column(String(100), ForeignKey("users.id"), nullable=False) job_id = Column(String(100), ForeignKey("jobs.id"), nullable=False) - cover_letter = Column(Text(1000), nullable=False) + cover_letter = Column(Text(5000), nullable=False) user = relationship('User', back_populates='applications') \ No newline at end of file diff --git a/web_flask/models/base_model.py b/web_flask/models/base_model.py index 58073ca..5a035fb 100644 --- a/web_flask/models/base_model.py +++ b/web_flask/models/base_model.py @@ -42,8 +42,8 @@ def __str__(self): def save(self): """Updates the updated_at attribute to current time""" self.updated_at = datetime.now() - models.storage.new(self) - models.storage.save() + models.storage.new(self) # Add a new instance to the session + models.storage.save() # Commit changes to the database def to_dict(self): """Return a dictionary representation of the BaseModel instance. @@ -60,4 +60,13 @@ def to_dict(self): def delete(self): """Delete the current instance from storage.""" - models.storage.delete(self) \ No newline at end of file + models.storage.delete(self) + + def update(self, **kwargs): + """Update the instance with the provided keyword arguments.""" + for key, value in kwargs.items(): + if key != '__class__': + if key in ['created_at', 'updated_at']: + value = datetime.fromisoformat(value) + setattr(self, key, value) + self.save() \ No newline at end of file diff --git a/web_flask/routes/application.py b/web_flask/routes/application.py index 73c5ef4..1af9c7e 100644 --- a/web_flask/routes/application.py +++ b/web_flask/routes/application.py @@ -21,6 +21,12 @@ def apply(job_id): "job_id": job_id, "cover_letter": form.cover_letter.data } + for item in current_user.applications: + print(item) + if item.job_id == job_id: + flash('You already applied for this job', 'success') + # return render_template('profile.html') + return render_template('apply.html', job_id=job_id, form=form) new_application = Application(**data) # Save the application to the database new_application.save() @@ -46,6 +52,7 @@ def get_applications(job_id): apps_data.append(app) return render_template('application-list.html', applications=apps_data) + @applications.route('/application/', methods=['GET'], strict_slashes=False) @login_required def single_app(app_id): diff --git a/web_flask/routes/job.py b/web_flask/routes/job.py index 1f76411..ce43505 100644 --- a/web_flask/routes/job.py +++ b/web_flask/routes/job.py @@ -17,7 +17,7 @@ def home(): jobs = storage.all(Job).values() for index, job in enumerate(jobs): # Limit the loop to the first 9 iterations - if index >= 9: + if index >= 7: break user = storage.get(User, job.user_id) if user: diff --git a/web_flask/routes/user.py b/web_flask/routes/user.py index 77c2eb9..994ae20 100644 --- a/web_flask/routes/user.py +++ b/web_flask/routes/user.py @@ -29,6 +29,7 @@ def login(): if user and bcrypt.check_password_hash(user.password, password): login_user(user) return redirect(url_for('job.home')) + flash('Invalid username or password', 'error') return render_template('login.html', form=form) @@ -68,8 +69,7 @@ def register(): flash("User Created Successfully", "success") # Rediect to Loggin Page return redirect(url_for('user.login')) - flash("Fill in all fields", "success") - return render_template('register.html', form=form) + return render_template('register.html', form=form,) @user.route("/logout") @@ -101,6 +101,7 @@ def single_user(user_id): return render_template('404.html') @user.route('/user/', methods=['POST'], strict_slashes=False) +@login_required def delete_user(user_id): """Delete User route - delete a user account""" user = storage.get(User, user_id) @@ -109,4 +110,31 @@ def delete_user(user_id): storage.save() return redirect(url_for('user.register')) else: - return render_template('404.html') \ No newline at end of file + return render_template('404.html') + + +# @user.route('/user/update/', methods=['GET', 'POST']) +# @login_required +# def update_user(user_id): +# """Update route""" +# user = storage.get(User, user_id) +# if not user: +# flash("User not found", "error") +# return redirect(url_for('job.home')) +# form = UpdateForm(obj=user) +# if form.validate_on_submit(): +# """Handle form submission logic here""" +# # extract data from form +# data = { +# "first_name": form.firstname.data, +# "last_name": form.lastname.data, +# "password": bcrypt.generate_password_hash(form.password.data).decode('utf-8'), +# "portfolio_url": form.portfolio_url.data, +# "github_url": form.github_url.data +# } +# # Create new User +# user.update(**data) +# flash("User Updated Successfully", "success") +# # Rediect to Loggin Page +# return redirect(url_for('job.home')) +# return render_template('update_user.html', form=form, user=current_user) \ No newline at end of file diff --git a/web_flask/static/css/apply.css b/web_flask/static/css/apply.css index 2694c3a..d3b88e6 100644 --- a/web_flask/static/css/apply.css +++ b/web_flask/static/css/apply.css @@ -82,7 +82,7 @@ border-color: var(--primary-color); cursor: pointer; transition: ease-in-out 0.8s; - font-family: sans-serif; + font-family: var(--main-font); border: none; } diff --git a/web_flask/static/css/base.css b/web_flask/static/css/base.css index da632bd..633fbab 100644 --- a/web_flask/static/css/base.css +++ b/web_flask/static/css/base.css @@ -92,6 +92,30 @@ main { } /* main section end */ +/* flash message */ +.flash-message { + text-align: center; + width: 100%; + color: var(--white); + padding: 0.2rem; + background-color: #c7c702; +} + +/* .flash-message .success { + background-color: var(--primary-color); +} + +.flash-message .error { + text-align: center; + width: 100%; + color: var(--white); + padding: 0.2rem; + margin-top: 1rem; + background-color: var(--red); +} */ + +/* flash message end */ + /* Footer Section Begin */ footer { diff --git a/web_flask/static/css/jobs.css b/web_flask/static/css/jobs.css new file mode 100644 index 0000000..ba9b2f8 --- /dev/null +++ b/web_flask/static/css/jobs.css @@ -0,0 +1,31 @@ +@import url('variables.css'); + +.jobs-container .job-search { + width: 90%; + margin: 0 auto; +} + +.jobs-container .job-search input { + font-family: var(--primary-color); + background-color: var(--white); + color: var(--dark-grey); + border: solid 1px var(--light-grey); + padding: 0.5rem 0.5rem 0.5rem 1rem; + border-radius: 0.5rem; + border-width: 2px; +} + +.jobs-container .job-search input:first-child { + width: 30%; +} + +.jobs-container .job-search .search-submit { + background-color: var(--primary-color); + font-family: var(--main-font); + color: var(--white); + border: none; + padding: 0.55rem 1rem; + border-radius: 0.3rem; + cursor: pointer; + transition: 1s ease-in-out; +} \ No newline at end of file diff --git a/web_flask/static/gif/jobmanagement.gif b/web_flask/static/gif/jobmanagement.gif new file mode 100644 index 0000000..c7574c7 Binary files /dev/null and b/web_flask/static/gif/jobmanagement.gif differ diff --git a/web_flask/static/gif/partime filter.gif b/web_flask/static/gif/partime filter.gif new file mode 100644 index 0000000..b643ee9 Binary files /dev/null and b/web_flask/static/gif/partime filter.gif differ diff --git a/web_flask/static/gif/search.gif b/web_flask/static/gif/search.gif new file mode 100644 index 0000000..c873e1d Binary files /dev/null and b/web_flask/static/gif/search.gif differ diff --git a/web_flask/templates/apply.html b/web_flask/templates/apply.html index 4209653..1d153b5 100644 --- a/web_flask/templates/apply.html +++ b/web_flask/templates/apply.html @@ -6,6 +6,13 @@ {% endblock %} {% block title %}JobCircus - Apply{% endblock %} {% block content %} +{% with messages = get_flashed_messages() %} +{% if messages %} +
+

{{ messages[0] }}

+
+{% endif %} +{% endwith %}

Demi text

@@ -13,15 +20,6 @@

Demi text

Application Form

- {% with messages = get_flashed_messages() %} - {% if messages %} -
    - {% for message in messages %} -
  • {{ message }}
  • - {% endfor %} -
- {% endif %} - {% endwith %}
{{ form.hidden_tag() }}
diff --git a/web_flask/templates/jobs.html b/web_flask/templates/jobs.html index 9404c22..d32ade3 100644 --- a/web_flask/templates/jobs.html +++ b/web_flask/templates/jobs.html @@ -2,10 +2,15 @@ {% block styles %} + {% endblock %} {% block title %}JobCircus - Jobs{% endblock %} {% block content %} -
+
+ + + +

JOBS LISTING: diff --git a/web_flask/templates/login.html b/web_flask/templates/login.html index e8adca0..25e7119 100644 --- a/web_flask/templates/login.html +++ b/web_flask/templates/login.html @@ -5,6 +5,13 @@ {% endblock %} {% block title %}JobCircus - Login{% endblock %} {% block content %} +{% with messages = get_flashed_messages() %} +{% if messages %} +
+

{{ messages[0] }}

+
+{% endif %} +{% endwith %} diff --git a/web_flask/templates/register.html b/web_flask/templates/register.html index 76fffbd..b94fad2 100644 --- a/web_flask/templates/register.html +++ b/web_flask/templates/register.html @@ -5,6 +5,13 @@ {% endblock %} {% block title %}JobCircus - Register{% endblock %} {% block content %} +{% with messages = get_flashed_messages() %} +{% if messages %} +
+

{{ messages[0] }}

+
+{% endif %} +{% endwith %}

Welcome

@@ -32,11 +39,11 @@

Create Account

- {{ form.email(class="form-control", placeholder="Enter Url") }} + {{ form.portfolio_url(class="form-control", placeholder="Enter Portfolio Url") }}
- {{ form.email(class="form-control", placeholder="Enter Url") }} + {{ form.github_url(class="form-control", placeholder="Enter Github Url") }}
diff --git a/web_flask/templates/singlejob.html b/web_flask/templates/singlejob.html index 7cbdf55..60e417d 100644 --- a/web_flask/templates/singlejob.html +++ b/web_flask/templates/singlejob.html @@ -40,7 +40,7 @@

Requirements

- {% else %} + {% elif current_user.id != job.user_id and current_user.role == 'Job-Seeker' %} Apply {% endif %}